Browse code

clean up of redirector code - first stage of RTP multicast support - added SDP redirector output for multicast

Originally committed as revision 1257 to svn://svn.ffmpeg.org/ffmpeg/trunk

Fabrice Bellard authored on 2002/11/21 00:00:05
Showing 1 changed files
... ...
@@ -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",