Originally committed as revision 602 to svn://svn.ffmpeg.org/ffmpeg/trunk
Philip Gladstone authored on 2002/05/26 12:36:34... | ... |
@@ -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"; |