Originally committed as revision 1257 to svn://svn.ffmpeg.org/ffmpeg/trunk
Fabrice Bellard authored on 2002/11/21 00:00:05... | ... |
@@ -192,6 +192,11 @@ typedef struct FFStream { |
192 | 192 |
struct FFStream *next; |
193 | 193 |
/* RTSP options */ |
194 | 194 |
char *rtsp_option; |
195 |
+ /* multicast specific */ |
|
196 |
+ int is_multicast; |
|
197 |
+ struct in_addr multicast_ip; |
|
198 |
+ int multicast_port; /* first port used for multicast */ |
|
199 |
+ |
|
195 | 200 |
/* feed specific */ |
196 | 201 |
int feed_opened; /* true if someone is writing to the feed */ |
197 | 202 |
int is_feed; /* true if it is a feed */ |
... | ... |
@@ -237,6 +242,10 @@ static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h); |
237 | 237 |
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h); |
238 | 238 |
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h); |
239 | 239 |
|
240 |
+/* SDP handling */ |
|
241 |
+static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer, |
|
242 |
+ struct in_addr my_ip); |
|
243 |
+ |
|
240 | 244 |
/* RTP handling */ |
241 | 245 |
static HTTPContext *rtp_new_connection(HTTPContext *rtsp_c, |
242 | 246 |
FFStream *stream, const char *session_id); |
... | ... |
@@ -1001,15 +1010,47 @@ static int validate_acl(FFStream *stream, HTTPContext *c) |
1001 | 1001 |
return (last_action == IP_DENY) ? 1 : 0; |
1002 | 1002 |
} |
1003 | 1003 |
|
1004 |
+/* compute the real filename of a file by matching it without its |
|
1005 |
+ extensions to all the stream filenames */ |
|
1006 |
+static void compute_real_filename(char *filename, int max_size) |
|
1007 |
+{ |
|
1008 |
+ char file1[1024]; |
|
1009 |
+ char file2[1024]; |
|
1010 |
+ char *p; |
|
1011 |
+ FFStream *stream; |
|
1012 |
+ |
|
1013 |
+ /* compute filename by matching without the file extensions */ |
|
1014 |
+ pstrcpy(file1, sizeof(file1), filename); |
|
1015 |
+ p = strrchr(file1, '.'); |
|
1016 |
+ if (p) |
|
1017 |
+ *p = '\0'; |
|
1018 |
+ for(stream = first_stream; stream != NULL; stream = stream->next) { |
|
1019 |
+ pstrcpy(file2, sizeof(file2), stream->filename); |
|
1020 |
+ p = strrchr(file2, '.'); |
|
1021 |
+ if (p) |
|
1022 |
+ *p = '\0'; |
|
1023 |
+ if (!strcmp(file1, file2)) { |
|
1024 |
+ pstrcpy(filename, max_size, stream->filename); |
|
1025 |
+ break; |
|
1026 |
+ } |
|
1027 |
+ } |
|
1028 |
+} |
|
1029 |
+ |
|
1030 |
+enum RedirType { |
|
1031 |
+ REDIR_NONE, |
|
1032 |
+ REDIR_ASX, |
|
1033 |
+ REDIR_RAM, |
|
1034 |
+ REDIR_ASF, |
|
1035 |
+ REDIR_RTSP, |
|
1036 |
+ REDIR_SDP, |
|
1037 |
+}; |
|
1038 |
+ |
|
1004 | 1039 |
/* parse http request and prepare header */ |
1005 | 1040 |
static int http_parse_request(HTTPContext *c) |
1006 | 1041 |
{ |
1007 | 1042 |
char *p; |
1008 | 1043 |
int post; |
1009 |
- int doing_asx; |
|
1010 |
- int doing_asf_redirector; |
|
1011 |
- int doing_ram; |
|
1012 |
- int doing_rtsp_redirector; |
|
1044 |
+ enum RedirType redir_type; |
|
1013 | 1045 |
char cmd[32]; |
1014 | 1046 |
char info[1024], *filename; |
1015 | 1047 |
char url[1024], *q; |
... | ... |
@@ -1068,57 +1109,27 @@ static int http_parse_request(HTTPContext *c) |
1068 | 1068 |
p++; |
1069 | 1069 |
} |
1070 | 1070 |
|
1071 |
- if (strlen(filename) > 4 && strcmp(".asx", filename + strlen(filename) - 4) == 0) { |
|
1072 |
- doing_asx = 1; |
|
1071 |
+ redir_type = REDIR_NONE; |
|
1072 |
+ if (match_ext(filename, "asx")) { |
|
1073 |
+ redir_type = REDIR_ASX; |
|
1073 | 1074 |
filename[strlen(filename)-1] = 'f'; |
1074 |
- } else { |
|
1075 |
- doing_asx = 0; |
|
1076 |
- } |
|
1077 |
- |
|
1078 |
- if (strlen(filename) > 4 && strcmp(".asf", filename + strlen(filename) - 4) == 0 && |
|
1075 |
+ } else if (match_ext(filename, ".asf") && |
|
1079 | 1076 |
(!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) { |
1080 | 1077 |
/* if this isn't WMP or lookalike, return the redirector file */ |
1081 |
- doing_asf_redirector = 1; |
|
1082 |
- } else { |
|
1083 |
- doing_asf_redirector = 0; |
|
1084 |
- } |
|
1085 |
- |
|
1086 |
- if (strlen(filename) > 4 && |
|
1087 |
- (strcmp(".rpm", filename + strlen(filename) - 4) == 0 || |
|
1088 |
- strcmp(".ram", filename + strlen(filename) - 4) == 0)) { |
|
1089 |
- doing_ram = 1; |
|
1078 |
+ redir_type = REDIR_ASF; |
|
1079 |
+ } else if (match_ext(filename, "rpm,ram")) { |
|
1080 |
+ redir_type = REDIR_RAM; |
|
1090 | 1081 |
strcpy(filename + strlen(filename)-2, "m"); |
1091 |
- } else { |
|
1092 |
- doing_ram = 0; |
|
1082 |
+ } else if (match_ext(filename, "rtsp")) { |
|
1083 |
+ redir_type = REDIR_RTSP; |
|
1084 |
+ compute_real_filename(filename, sizeof(url) - 1); |
|
1085 |
+ } else if (match_ext(filename, "sdp")) { |
|
1086 |
+ redir_type = REDIR_SDP; |
|
1087 |
+ printf("before %s\n", filename); |
|
1088 |
+ compute_real_filename(filename, sizeof(url) - 1); |
|
1089 |
+ printf("after %s\n", filename); |
|
1093 | 1090 |
} |
1094 |
- |
|
1095 |
- if (strlen(filename) > 5 && |
|
1096 |
- strcmp(".rtsp", filename + strlen(filename) - 5) == 0) { |
|
1097 |
- char file1[1024]; |
|
1098 |
- char file2[1024]; |
|
1099 |
- char *p; |
|
1100 |
- |
|
1101 |
- doing_rtsp_redirector = 1; |
|
1102 |
- /* compute filename by matching without the file extensions */ |
|
1103 |
- pstrcpy(file1, sizeof(file1), filename); |
|
1104 |
- p = strrchr(file1, '.'); |
|
1105 |
- if (p) |
|
1106 |
- *p = '\0'; |
|
1107 |
- for(stream = first_stream; stream != NULL; stream = stream->next) { |
|
1108 |
- pstrcpy(file2, sizeof(file2), stream->filename); |
|
1109 |
- p = strrchr(file2, '.'); |
|
1110 |
- if (p) |
|
1111 |
- *p = '\0'; |
|
1112 |
- if (!strcmp(file1, file2)) { |
|
1113 |
- pstrcpy(url, sizeof(url), stream->filename); |
|
1114 |
- filename = url; |
|
1115 |
- break; |
|
1116 |
- } |
|
1117 |
- } |
|
1118 |
- } else { |
|
1119 |
- doing_rtsp_redirector = 0; |
|
1120 |
- } |
|
1121 |
- |
|
1091 |
+ |
|
1122 | 1092 |
stream = first_stream; |
1123 | 1093 |
while (stream != NULL) { |
1124 | 1094 |
if (!strcmp(stream->filename, filename) && validate_acl(stream, c)) |
... | ... |
@@ -1201,8 +1212,7 @@ static int http_parse_request(HTTPContext *c) |
1201 | 1201 |
return 0; |
1202 | 1202 |
} |
1203 | 1203 |
|
1204 |
- if (doing_asx || doing_ram || doing_asf_redirector || |
|
1205 |
- doing_rtsp_redirector) { |
|
1204 |
+ if (redir_type != REDIR_NONE) { |
|
1206 | 1205 |
char *hostinfo = 0; |
1207 | 1206 |
|
1208 | 1207 |
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) { |
... | ... |
@@ -1235,7 +1245,8 @@ static int http_parse_request(HTTPContext *c) |
1235 | 1235 |
|
1236 | 1236 |
c->http_error = 200; |
1237 | 1237 |
q = c->buffer; |
1238 |
- if (doing_asx) { |
|
1238 |
+ switch(redir_type) { |
|
1239 |
+ case REDIR_ASX: |
|
1239 | 1240 |
q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n"); |
1240 | 1241 |
q += sprintf(q, "Content-type: video/x-ms-asf\r\n"); |
1241 | 1242 |
q += sprintf(q, "\r\n"); |
... | ... |
@@ -1244,36 +1255,68 @@ static int http_parse_request(HTTPContext *c) |
1244 | 1244 |
q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n", |
1245 | 1245 |
hostbuf, filename, info); |
1246 | 1246 |
q += sprintf(q, "</ASX>\r\n"); |
1247 |
- } else if (doing_ram) { |
|
1247 |
+ break; |
|
1248 |
+ case REDIR_RAM: |
|
1248 | 1249 |
q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n"); |
1249 | 1250 |
q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n"); |
1250 | 1251 |
q += sprintf(q, "\r\n"); |
1251 | 1252 |
q += sprintf(q, "# Autogenerated by ffserver\r\n"); |
1252 | 1253 |
q += sprintf(q, "http://%s/%s%s\r\n", |
1253 | 1254 |
hostbuf, filename, info); |
1254 |
- } else if (doing_asf_redirector) { |
|
1255 |
+ break; |
|
1256 |
+ case REDIR_ASF: |
|
1255 | 1257 |
q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n"); |
1256 | 1258 |
q += sprintf(q, "Content-type: video/x-ms-asf\r\n"); |
1257 | 1259 |
q += sprintf(q, "\r\n"); |
1258 | 1260 |
q += sprintf(q, "[Reference]\r\n"); |
1259 | 1261 |
q += sprintf(q, "Ref1=http://%s/%s%s\r\n", |
1260 | 1262 |
hostbuf, filename, info); |
1261 |
- } else if (doing_rtsp_redirector) { |
|
1262 |
- char hostname[256], *p; |
|
1263 |
- /* extract only hostname */ |
|
1264 |
- pstrcpy(hostname, sizeof(hostname), hostbuf); |
|
1265 |
- p = strrchr(hostname, ':'); |
|
1266 |
- if (p) |
|
1267 |
- *p = '\0'; |
|
1268 |
- q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n"); |
|
1269 |
- /* XXX: incorrect mime type ? */ |
|
1270 |
- q += sprintf(q, "Content-type: application/x-rtsp\r\n"); |
|
1271 |
- q += sprintf(q, "\r\n"); |
|
1272 |
- q += sprintf(q, "rtsp://%s:%d/%s\r\n", |
|
1273 |
- hostname, ntohs(my_rtsp_addr.sin_port), |
|
1274 |
- filename); |
|
1275 |
- } else { |
|
1263 |
+ break; |
|
1264 |
+ case REDIR_RTSP: |
|
1265 |
+ { |
|
1266 |
+ char hostname[256], *p; |
|
1267 |
+ /* extract only hostname */ |
|
1268 |
+ pstrcpy(hostname, sizeof(hostname), hostbuf); |
|
1269 |
+ p = strrchr(hostname, ':'); |
|
1270 |
+ if (p) |
|
1271 |
+ *p = '\0'; |
|
1272 |
+ q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n"); |
|
1273 |
+ /* XXX: incorrect mime type ? */ |
|
1274 |
+ q += sprintf(q, "Content-type: application/x-rtsp\r\n"); |
|
1275 |
+ q += sprintf(q, "\r\n"); |
|
1276 |
+ q += sprintf(q, "rtsp://%s:%d/%s\r\n", |
|
1277 |
+ hostname, ntohs(my_rtsp_addr.sin_port), |
|
1278 |
+ filename); |
|
1279 |
+ } |
|
1280 |
+ break; |
|
1281 |
+ case REDIR_SDP: |
|
1282 |
+ { |
|
1283 |
+ UINT8 *sdp_data; |
|
1284 |
+ int sdp_data_size, len; |
|
1285 |
+ struct sockaddr_in my_addr; |
|
1286 |
+ |
|
1287 |
+ q += sprintf(q, "HTTP/1.0 200 OK\r\n"); |
|
1288 |
+ q += sprintf(q, "Content-type: application/sdp\r\n"); |
|
1289 |
+ q += sprintf(q, "\r\n"); |
|
1290 |
+ |
|
1291 |
+ len = sizeof(my_addr); |
|
1292 |
+ getsockname(c->fd, (struct sockaddr *)&my_addr, &len); |
|
1293 |
+ |
|
1294 |
+ /* XXX: should use a dynamic buffer */ |
|
1295 |
+ sdp_data_size = prepare_sdp_description(stream, |
|
1296 |
+ &sdp_data, |
|
1297 |
+ my_addr.sin_addr); |
|
1298 |
+ if (sdp_data_size > 0) { |
|
1299 |
+ memcpy(q, sdp_data, sdp_data_size); |
|
1300 |
+ q += sdp_data_size; |
|
1301 |
+ *q = '\0'; |
|
1302 |
+ av_free(sdp_data); |
|
1303 |
+ } |
|
1304 |
+ } |
|
1305 |
+ break; |
|
1306 |
+ default: |
|
1276 | 1307 |
av_abort(); |
1308 |
+ break; |
|
1277 | 1309 |
} |
1278 | 1310 |
|
1279 | 1311 |
/* prepare output buffer */ |
... | ... |
@@ -1483,12 +1526,16 @@ static void compute_stats(HTTPContext *c) |
1483 | 1483 |
} else if (strcmp(eosf - 3, ".rm") == 0) { |
1484 | 1484 |
strcpy(eosf - 3, ".ram"); |
1485 | 1485 |
} else if (stream->fmt == &rtp_mux) { |
1486 |
- /* generate a sample RTSP director - maybe should |
|
1487 |
- generate a .sdp file ? */ |
|
1486 |
+ /* generate a sample RTSP director if |
|
1487 |
+ unicast. Generate an SDP redirector if |
|
1488 |
+ multicast */ |
|
1488 | 1489 |
eosf = strrchr(sfilename, '.'); |
1489 | 1490 |
if (!eosf) |
1490 | 1491 |
eosf = sfilename + strlen(sfilename); |
1491 |
- strcpy(eosf, ".rtsp"); |
|
1492 |
+ if (stream->is_multicast) |
|
1493 |
+ strcpy(eosf, ".sdp"); |
|
1494 |
+ else |
|
1495 |
+ strcpy(eosf, ".rtsp"); |
|
1492 | 1496 |
} |
1493 | 1497 |
} |
1494 | 1498 |
|
... | ... |
@@ -2492,25 +2539,23 @@ static int rtsp_parse_request(HTTPContext *c) |
2492 | 2492 |
return 0; |
2493 | 2493 |
} |
2494 | 2494 |
|
2495 |
-static int prepare_sdp_description(HTTPContext *c, |
|
2496 |
- FFStream *stream, UINT8 **pbuffer) |
|
2495 |
+/* XXX: move that to rtsp.c, but would need to replace FFStream by |
|
2496 |
+ AVFormatContext */ |
|
2497 |
+static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer, |
|
2498 |
+ struct in_addr my_ip) |
|
2497 | 2499 |
{ |
2498 | 2500 |
ByteIOContext pb1, *pb = &pb1; |
2499 |
- struct sockaddr_in my_addr; |
|
2500 |
- int len, i, payload_type; |
|
2501 |
+ int i, payload_type, port; |
|
2501 | 2502 |
const char *ipstr, *title, *mediatype; |
2502 | 2503 |
AVStream *st; |
2503 | 2504 |
|
2504 |
- len = sizeof(my_addr); |
|
2505 |
- getsockname(c->fd, (struct sockaddr *)&my_addr, &len); |
|
2506 |
- ipstr = inet_ntoa(my_addr.sin_addr); |
|
2507 |
- |
|
2508 | 2505 |
if (url_open_dyn_buf(pb) < 0) |
2509 | 2506 |
return -1; |
2510 | 2507 |
|
2511 | 2508 |
/* general media info */ |
2512 | 2509 |
|
2513 | 2510 |
url_fprintf(pb, "v=0\n"); |
2511 |
+ ipstr = inet_ntoa(my_ip); |
|
2514 | 2512 |
url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr); |
2515 | 2513 |
title = stream->title; |
2516 | 2514 |
if (title[0] == '\0') |
... | ... |
@@ -2518,7 +2563,9 @@ static int prepare_sdp_description(HTTPContext *c, |
2518 | 2518 |
url_fprintf(pb, "s=%s\n", title); |
2519 | 2519 |
if (stream->comment[0] != '\0') |
2520 | 2520 |
url_fprintf(pb, "i=%s\n", stream->comment); |
2521 |
- |
|
2521 |
+ if (stream->is_multicast) { |
|
2522 |
+ url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip)); |
|
2523 |
+ } |
|
2522 | 2524 |
/* for each stream, we output the necessary info */ |
2523 | 2525 |
for(i = 0; i < stream->nb_streams; i++) { |
2524 | 2526 |
st = stream->streams[i]; |
... | ... |
@@ -2533,12 +2580,16 @@ static int prepare_sdp_description(HTTPContext *c, |
2533 | 2533 |
mediatype = "application"; |
2534 | 2534 |
break; |
2535 | 2535 |
} |
2536 |
- /* XXX: the port indication is not correct (but should be correct |
|
2537 |
- for broadcast) */ |
|
2536 |
+ /* NOTE: the port indication is not correct in case of |
|
2537 |
+ unicast. It is not an issue because RTSP gives it */ |
|
2538 | 2538 |
payload_type = rtp_get_payload_type(&st->codec); |
2539 |
- |
|
2539 |
+ if (stream->is_multicast) { |
|
2540 |
+ port = stream->multicast_port + 2 * i; |
|
2541 |
+ } else { |
|
2542 |
+ port = 0; |
|
2543 |
+ } |
|
2540 | 2544 |
url_fprintf(pb, "m=%s %d RTP/AVP %d\n", |
2541 |
- mediatype, 0, payload_type); |
|
2545 |
+ mediatype, port, payload_type); |
|
2542 | 2546 |
url_fprintf(pb, "a=control:streamid=%d\n", i); |
2543 | 2547 |
} |
2544 | 2548 |
return url_close_dyn_buf(pb, pbuffer); |
... | ... |
@@ -2550,7 +2601,8 @@ static void rtsp_cmd_describe(HTTPContext *c, const char *url) |
2550 | 2550 |
char path1[1024]; |
2551 | 2551 |
const char *path; |
2552 | 2552 |
UINT8 *content; |
2553 |
- int content_length; |
|
2553 |
+ int content_length, len; |
|
2554 |
+ struct sockaddr_in my_addr; |
|
2554 | 2555 |
|
2555 | 2556 |
/* find which url is asked */ |
2556 | 2557 |
url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); |
... | ... |
@@ -2570,7 +2622,12 @@ static void rtsp_cmd_describe(HTTPContext *c, const char *url) |
2570 | 2570 |
|
2571 | 2571 |
found: |
2572 | 2572 |
/* prepare the media description in sdp format */ |
2573 |
- content_length = prepare_sdp_description(c, stream, &content); |
|
2573 |
+ |
|
2574 |
+ /* get the host IP */ |
|
2575 |
+ len = sizeof(my_addr); |
|
2576 |
+ getsockname(c->fd, (struct sockaddr *)&my_addr, &len); |
|
2577 |
+ |
|
2578 |
+ content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr); |
|
2574 | 2579 |
if (content_length < 0) { |
2575 | 2580 |
rtsp_reply_error(c, RTSP_STATUS_INTERNAL); |
2576 | 2581 |
return; |
... | ... |
@@ -3886,6 +3943,21 @@ int parse_ffconfig(const char *filename) |
3886 | 3886 |
strcpy(stream->rtsp_option, arg); |
3887 | 3887 |
} |
3888 | 3888 |
} |
3889 |
+ } else if (!strcasecmp(cmd, "MulticastAddress")) { |
|
3890 |
+ get_arg(arg, sizeof(arg), &p); |
|
3891 |
+ if (stream) { |
|
3892 |
+ if (!inet_aton(arg, &stream->multicast_ip)) { |
|
3893 |
+ fprintf(stderr, "%s:%d: Invalid IP address: %s\n", |
|
3894 |
+ filename, line_num, arg); |
|
3895 |
+ errors++; |
|
3896 |
+ } |
|
3897 |
+ stream->is_multicast = 1; |
|
3898 |
+ } |
|
3899 |
+ } else if (!strcasecmp(cmd, "MulticastPort")) { |
|
3900 |
+ get_arg(arg, sizeof(arg), &p); |
|
3901 |
+ if (stream) { |
|
3902 |
+ stream->multicast_port = atoi(arg); |
|
3903 |
+ } |
|
3889 | 3904 |
} else if (!strcasecmp(cmd, "</Stream>")) { |
3890 | 3905 |
if (!stream) { |
3891 | 3906 |
fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n", |