Browse code

* Add first cut of code to handle Windows Media Player rate switching requests. The current state is that at startup, WMP will get the best stream that it can handle. However, subsequent rate switching only puts a message in the log saying what the new stream ought to be. Solving this will be tricky. I guess that we would have to wait for key frames to appear in the new stream, and then switch over to it. Some care would be needed to deal with the PTS of the new stream versus the old stream.

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

Philip Gladstone authored on 2002/05/26 12:36:34
Showing 1 changed files
... ...
@@ -92,6 +92,7 @@ typedef struct HTTPContext {
92 92
     int suppress_log;
93 93
     int bandwidth;
94 94
     time_t start_time;
95
+    int wmp_client_id;
95 96
     char protocol[16];
96 97
     char method[16];
97 98
     char url[128];
... ...
@@ -195,8 +196,9 @@ static void log_connection(HTTPContext *c)
195 195
     p = buf2 + strlen(p) - 1;
196 196
     if (*p == '\n')
197 197
         *p = '\0';
198
-    http_log("%s - - [%s] \"%s %s %s\" %d %lld\n", 
199
-             buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
198
+    http_log("%s - - [%s] \"%s %s %s\" %d %lld %s\n", 
199
+             buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count,
200
+             c->stream ? c->stream->filename : "");
200 201
 }
201 202
 
202 203
 /* main loop of the http server */
... ...
@@ -446,6 +448,136 @@ static int handle_http(HTTPContext *c, long cur_time)
446 446
     return 0;
447 447
 }
448 448
 
449
+static int extract_rates(char *rates, int ratelen, const char *request)
450
+{
451
+    const char *p;
452
+
453
+    for (p = request; *p && *p != '\r' && *p != '\n'; ) {
454
+        if (strncasecmp(p, "Pragma:", 7) == 0) {
455
+            const char *q = p + 7;
456
+
457
+            while (*q && *q != '\n' && isspace(*q))
458
+                q++;
459
+
460
+            if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
461
+                int stream_no;
462
+                int rate_no;
463
+
464
+                q += 20;
465
+
466
+                memset(rates, 0, ratelen);
467
+
468
+                while (1) {
469
+                    while (*q && *q != '\n' && *q != ':')
470
+                        q++;
471
+
472
+                    if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
473
+                        break;
474
+                    }
475
+                    stream_no--;
476
+                    if (stream_no < ratelen && stream_no >= 0) {
477
+                        rates[stream_no] = rate_no;
478
+                    }
479
+
480
+                    while (*q && *q != '\n' && !isspace(*q))
481
+                        q++;
482
+                }
483
+
484
+                return 1;
485
+            }
486
+        }
487
+        p = strchr(p, '\n');
488
+        if (!p)
489
+            break;
490
+
491
+        p++;
492
+    }
493
+
494
+    return 0;
495
+}
496
+
497
+static FFStream *find_optimal_stream(FFStream *req, char *rates)
498
+{
499
+    int i;
500
+    FFStream *rover;
501
+    int req_bitrate = 0;
502
+    int want_bitrate = 0;
503
+    FFStream *best = 0;
504
+    int best_bitrate;
505
+
506
+    for (i = 0; i < req->nb_streams; i++) {
507
+        AVCodecContext *codec = &req->streams[i]->codec;
508
+
509
+        req_bitrate += codec->bit_rate;
510
+
511
+        switch(rates[i]) {
512
+            case 0:
513
+                want_bitrate += codec->bit_rate;
514
+                break;
515
+            case 1:
516
+                want_bitrate += codec->bit_rate / 2;
517
+                break;
518
+            case 2:
519
+                break;
520
+        }
521
+    }
522
+
523
+    best_bitrate = req_bitrate;
524
+    if (best_bitrate <= want_bitrate)
525
+        return 0;       /* We are OK */
526
+
527
+    /* Now we have the actual rates that we can use. Now find the stream that uses most of it! */
528
+
529
+    for (rover = first_stream; rover; rover = rover->next) {
530
+        if (rover->feed != req->feed ||
531
+            rover->fmt != req->fmt ||
532
+            rover->nb_streams != req->nb_streams ||
533
+            rover == req) {
534
+            continue;
535
+        }
536
+
537
+        /* Now see if the codecs all match */
538
+
539
+        for (i = 0; i < req->nb_streams; i++) {
540
+            AVCodecContext *codec = &req->streams[i]->codec;
541
+            AVCodecContext *rovercodec = &rover->streams[i]->codec;
542
+
543
+            if (rovercodec->codec_id != codec->codec_id ||
544
+                rovercodec->sample_rate != codec->sample_rate) {
545
+                /* Does the video width and height have to match?? */
546
+                break;
547
+            }
548
+        }
549
+
550
+        if (i == req->nb_streams) {
551
+            /* The rovercodec is another possible stream */
552
+            int rover_bitrate = 0;
553
+
554
+            for (i = 0; i < req->nb_streams; i++) {
555
+                AVCodecContext *codec = &rover->streams[i]->codec;
556
+
557
+                rover_bitrate += codec->bit_rate;
558
+            }
559
+
560
+            /* We want to choose the largest rover_bitrate <= want_bitrate, or the smallest
561
+             * rover_bitrate if none <= want_bitrate
562
+             */
563
+            if (rover_bitrate <= want_bitrate) {
564
+                if (best_bitrate > want_bitrate || rover_bitrate > best_bitrate) {
565
+                    best_bitrate = rover_bitrate;
566
+                    best = rover;
567
+                }
568
+            } else {
569
+                if (rover_bitrate < best_bitrate) {
570
+                    best_bitrate = rover_bitrate;
571
+                    best = rover;
572
+                }
573
+            }
574
+        }
575
+    }
576
+
577
+    return best;
578
+}
449 579
 
450 580
 /* parse http request and prepare header */
451 581
 static int http_parse_request(HTTPContext *c)
... ...
@@ -462,6 +594,7 @@ static int http_parse_request(HTTPContext *c)
462 462
     const char *mime_type;
463 463
     FFStream *stream;
464 464
     int i;
465
+    char ratebuf[32];
465 466
 
466 467
     p = c->buffer;
467 468
     q = cmd;
... ...
@@ -545,6 +678,15 @@ static int http_parse_request(HTTPContext *c)
545 545
         goto send_error;
546 546
     }
547 547
 
548
+    /* If this is WMP, get the rate information */
549
+    if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
550
+        FFStream *optimal;
551
+
552
+        optimal = find_optimal_stream(stream, ratebuf);
553
+        if (optimal)
554
+            stream = optimal;
555
+    }
556
+
548 557
     if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
549 558
         /* See if we meet the bandwidth requirements */
550 559
         for(i=0;i<stream->nb_streams;i++) {
... ...
@@ -661,12 +803,16 @@ static int http_parse_request(HTTPContext *c)
661 661
              * as it might come in handy one day
662 662
              */
663 663
             char *logline = 0;
664
+            int client_id = 0;
664 665
             
665 666
             for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
666 667
                 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
667 668
                     logline = p;
668 669
                     break;
669 670
                 }
671
+                if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
672
+                    client_id = strtol(p + 18, 0, 10);
673
+                }
670 674
                 p = strchr(p, '\n');
671 675
                 if (!p)
672 676
                     break;
... ...
@@ -686,6 +832,29 @@ static int http_parse_request(HTTPContext *c)
686 686
                     c->suppress_log = 1;
687 687
                 }
688 688
             }
689
+
690
+#ifdef DEBUG
691
+            fprintf(stderr, "\nGot request:\n%s\n", c->buffer);
692
+#endif
693
+
694
+            if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
695
+                HTTPContext *wmpc;
696
+
697
+                /* Now we have to find the client_id */
698
+                for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
699
+                    if (wmpc->wmp_client_id == client_id)
700
+                        break;
701
+                }
702
+
703
+                if (wmpc) {
704
+                    FFStream *optimal;
705
+                    optimal = find_optimal_stream(wmpc->stream, ratebuf);
706
+                    if (optimal) {
707
+                        fprintf(stderr, "Would like to switch stream from %s to %s\n",
708
+                                wmpc->stream->filename, optimal->filename);
709
+                    }
710
+                }
711
+            }
689 712
             
690 713
             sprintf(msg, "POST command not handled");
691 714
             goto send_error;
... ...
@@ -699,6 +868,12 @@ static int http_parse_request(HTTPContext *c)
699 699
         return 0;
700 700
     }
701 701
 
702
+#ifdef DEBUG
703
+    if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
704
+        fprintf(stderr, "\nGot request:\n%s\n", c->buffer);
705
+    }
706
+#endif
707
+
702 708
     if (c->stream->stream_type == STREAM_TYPE_STATUS)
703 709
         goto send_stats;
704 710
 
... ...
@@ -718,7 +893,15 @@ static int http_parse_request(HTTPContext *c)
718 718
 
719 719
     /* for asf, we need extra headers */
720 720
     if (!strcmp(c->stream->fmt->name,"asf")) {
721
-        q += sprintf(q, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=1234\r\nPragma: features=\"broadcast\"\r\n");
721
+        /* Need to allocate a client id */
722
+        static int wmp_session;
723
+
724
+        if (!wmp_session)
725
+            wmp_session = time(0) & 0xffffff;
726
+
727
+        c->wmp_client_id = ++wmp_session;
728
+
729
+        q += sprintf(q, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
722 730
         /* mime_type = "application/octet-stream"; */
723 731
         /* video/x-ms-asf seems better -- netscape doesn't crash any more! */
724 732
         mime_type = "video/x-ms-asf";