fix and simplify frame size check and reencoding in verbatim mode
[ffmpeg.git] / ffserver.c
index 493d22e..08b89a8 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#define _XOPEN_SOURCE 600
+
 #include "config.h"
 #ifndef HAVE_CLOSESOCKET
 #define closesocket close
 #endif
 #include <string.h>
+#include <strings.h>
 #include <stdlib.h>
 #include "libavutil/random.h"
 #include "libavutil/avstring.h"
@@ -348,7 +351,7 @@ static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
     if (level > av_log_level)
         return;
     if (print_prefix && avc)
-        http_log("[%s @ %p]", avc->item_name(ptr), avc);
+        http_log("[%s @ %p]", avc->item_name(ptr), ptr);
     print_prefix = strstr(fmt, "\n") != NULL;
     http_vlog(fmt, vargs);
 }
@@ -407,6 +410,21 @@ static void start_children(FFStream *feed)
                 char *slash;
                 int i;
 
+                av_strlcpy(pathname, my_program_name, sizeof(pathname));
+
+                slash = strrchr(pathname, '/');
+                if (!slash)
+                    slash = pathname;
+                else
+                    slash++;
+                strcpy(slash, "ffmpeg");
+
+                http_log("Launch commandline: ");
+                http_log("%s ", pathname);
+                for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
+                    http_log("%s ", feed->child_argv[i]);
+                http_log("\n");
+
                 for (i = 3; i < 256; i++)
                     close(i);
 
@@ -420,15 +438,6 @@ static void start_children(FFStream *feed)
                     }
                 }
 
-                av_strlcpy(pathname, my_program_name, sizeof(pathname));
-
-                slash = strrchr(pathname, '/');
-                if (!slash)
-                    slash = pathname;
-                else
-                    slash++;
-                strcpy(slash, "ffmpeg");
-
                 /* This is needed to make relative pathnames work */
                 chdir(my_program_dir);
 
@@ -558,9 +567,6 @@ static int http_server(void)
 
     start_children(first_feed);
 
-    first_http_ctx = NULL;
-    nb_connections = 0;
-
     start_multicast();
 
     for(;;) {
@@ -1341,6 +1347,7 @@ static int http_parse_request(HTTPContext *c)
     /* If already streaming this feed, do not let start another feeder. */
     if (stream->feed_opened) {
         snprintf(msg, sizeof(msg), "This feed is already being received.");
+        http_log("feed %s already being received\n", stream->feed_filename);
         goto send_error;
     }
 
@@ -1493,9 +1500,8 @@ static int http_parse_request(HTTPContext *c)
     if (c->post) {
         /* if post, it means a feed is being sent */
         if (!stream->is_feed) {
-            /* However it might be a status report from WMP! Lets log the data
-             * as it might come in handy one day
-             */
+            /* However it might be a status report from WMP! Let us log the
+             * data as it might come in handy one day. */
             char *logline = 0;
             int client_id = 0;
 
@@ -1692,8 +1698,7 @@ static void compute_status(HTTPContext *c)
                         stream->conns_served);
             fmt_bytecount(pb, stream->bytes_served);
             switch(stream->stream_type) {
-            case STREAM_TYPE_LIVE:
-                {
+            case STREAM_TYPE_LIVE: {
                     int audio_bit_rate = 0;
                     int video_bit_rate = 0;
                     const char *audio_codec_name = "";
@@ -1931,13 +1936,11 @@ static int open_input_stream(HTTPContext *c, const char *info)
         strcpy(input_filename, c->stream->feed->feed_filename);
         buf_size = FFM_PACKET_SIZE;
         /* compute position (absolute time) */
-        if (find_info_tag(buf, sizeof(buf), "date", info))
-        {
+        if (find_info_tag(buf, sizeof(buf), "date", info)) {
             stream_pos = parse_date(buf, 0);
             if (stream_pos == INT64_MIN)
                 return -1;
-        }
-        else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
+        } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
             int prebuffer = strtol(buf, 0, 10);
             stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
         } else
@@ -1946,13 +1949,11 @@ static int open_input_stream(HTTPContext *c, const char *info)
         strcpy(input_filename, c->stream->feed_filename);
         buf_size = 0;
         /* compute position (relative time) */
-        if (find_info_tag(buf, sizeof(buf), "date", info))
-        {
+        if (find_info_tag(buf, sizeof(buf), "date", info)) {
             stream_pos = parse_date(buf, 1);
             if (stream_pos == INT64_MIN)
                 return -1;
-        }
-        else
+        } else
             stream_pos = 0;
     }
     if (input_filename[0] == '\0')
@@ -2040,10 +2041,7 @@ static int http_prepare_data(HTTPContext *c)
         av_strlcpy(c->fmt_ctx.title, c->stream->title,
                    sizeof(c->fmt_ctx.title));
 
-        /* open output stream by using specified codecs */
-        c->fmt_ctx.oformat = c->stream->fmt;
-        c->fmt_ctx.nb_streams = c->stream->nb_streams;
-        for(i=0;i<c->fmt_ctx.nb_streams;i++) {
+        for(i=0;i<c->stream->nb_streams;i++) {
             AVStream *st;
             AVStream *src;
             st = av_mallocz(sizeof(AVStream));
@@ -2060,6 +2058,10 @@ static int http_prepare_data(HTTPContext *c)
             st->codec->frame_number = 0; /* XXX: should be done in
                                            AVStream, not in codec */
         }
+        /* set output format parameters */
+        c->fmt_ctx.oformat = c->stream->fmt;
+        c->fmt_ctx.nb_streams = c->stream->nb_streams;
+
         c->got_key_frame = 0;
 
         /* prepare header and save header data in a stream */
@@ -2158,19 +2160,18 @@ static int http_prepare_data(HTTPContext *c)
                     }
                 } else {
                     AVCodecContext *codec;
-
+                    AVStream *ist, *ost;
                 send_it:
+                    ist = c->fmt_in->streams[source_index];
                     /* specific handling for RTP: we use several
                        output stream (one for each RTP
                        connection). XXX: need more abstract handling */
                     if (c->is_packetized) {
-                        AVStream *st;
                         /* compute send time and duration */
-                        st = c->fmt_in->streams[pkt.stream_index];
-                        c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
-                        if (st->start_time != AV_NOPTS_VALUE)
-                            c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
-                        c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
+                        c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
+                        if (ist->start_time != AV_NOPTS_VALUE)
+                            c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
+                        c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
 #if 0
                         printf("index=%d pts=%0.3f duration=%0.6f\n",
                                pkt.stream_index,
@@ -2209,22 +2210,17 @@ static int http_prepare_data(HTTPContext *c)
                         /* XXX: potential leak */
                         return -1;
                     }
-                    c->fmt_ctx.pb->is_streamed = 1;
+                    ost = ctx->streams[pkt.stream_index];
+
+                    ctx->pb->is_streamed = 1;
                     if (pkt.dts != AV_NOPTS_VALUE)
-                        pkt.dts = av_rescale_q(pkt.dts,
-                                               c->fmt_in->streams[source_index]->time_base,
-                                               ctx->streams[pkt.stream_index]->time_base);
+                        pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
                     if (pkt.pts != AV_NOPTS_VALUE)
-                        pkt.pts = av_rescale_q(pkt.pts,
-                                               c->fmt_in->streams[source_index]->time_base,
-                                               ctx->streams[pkt.stream_index]->time_base);
-                    pkt.duration = av_rescale_q(pkt.duration,
-                                                c->fmt_in->streams[source_index]->time_base,
-                                                ctx->streams[pkt.stream_index]->time_base);
+                        pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
+                    pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
                     if (av_write_frame(ctx, &pkt) < 0) {
                         http_log("Error writing frame to output\n");
                         c->state = HTTPSTATE_SEND_DATA_TRAILER;
-                        return 1;
                     }
 
                     len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
@@ -2487,15 +2483,18 @@ static int http_receive_data(HTTPContext *c)
             AVInputFormat *fmt_in;
             int i;
 
-            url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
-            pb->is_streamed = 1;
-
             /* use feed output format name to find corresponding input format */
             fmt_in = av_find_input_format(feed->fmt->name);
             if (!fmt_in)
                 goto fail;
 
-            av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL);
+            url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
+            pb->is_streamed = 1;
+
+            if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
+                av_free(pb);
+                goto fail;
+            }
 
             /* Now we have the actual streams */
             if (s->nb_streams != feed->nb_streams) {
@@ -2504,9 +2503,18 @@ static int http_receive_data(HTTPContext *c)
                 goto fail;
             }
 
-            for (i = 0; i < s->nb_streams; i++)
-                memcpy(feed->streams[i]->codec,
-                       s->streams[i]->codec, sizeof(AVCodecContext));
+            for (i = 0; i < s->nb_streams; i++) {
+                AVStream *fst = feed->streams[i];
+                AVStream *st = s->streams[i];
+                memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
+                if (fst->codec->extradata_size) {
+                    fst->codec->extradata = av_malloc(fst->codec->extradata_size);
+                    if (!fst->codec->extradata)
+                        goto fail;
+                    memcpy(fst->codec->extradata, st->codec->extradata,
+                           fst->codec->extradata_size);
+                }
+            }
 
             av_close_input_stream(s);
             av_free(pb);
@@ -3132,7 +3140,6 @@ static int rtp_new_av_stream(HTTPContext *c,
     char *ipaddr;
     URLContext *h = NULL;
     uint8_t *dummy_buf;
-    char buf2[32];
     int max_packet_size;
 
     /* now we can open the relevant output stream */
@@ -3193,9 +3200,8 @@ static int rtp_new_av_stream(HTTPContext *c,
         goto fail;
     }
 
-    http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
+    http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
              ipaddr, ntohs(dest_addr->sin_port),
-             ctime1(buf2),
              c->stream->filename, stream_index, c->protocol);
 
     /* normally, no packets should be output here, but the packet size may be checked */
@@ -3439,7 +3445,7 @@ static void build_feed_streams(void)
 
                         if (sf->index != ss->index ||
                             sf->id != ss->id) {
-                            printf("Index & Id do not match for stream %d (%s)\n",
+                            http_log("Index & Id do not match for stream %d (%s)\n",
                                    i, feed->feed_filename);
                             matches = 0;
                         } else {
@@ -3450,28 +3456,28 @@ static void build_feed_streams(void)
 #define CHECK_CODEC(x)  (ccf->x != ccs->x)
 
                             if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
-                                printf("Codecs do not match for stream %d\n", i);
+                                http_log("Codecs do not match for stream %d\n", i);
                                 matches = 0;
                             } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
-                                printf("Codec bitrates do not match for stream %d\n", i);
+                                http_log("Codec bitrates do not match for stream %d\n", i);
                                 matches = 0;
                             } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
                                 if (CHECK_CODEC(time_base.den) ||
                                     CHECK_CODEC(time_base.num) ||
                                     CHECK_CODEC(width) ||
                                     CHECK_CODEC(height)) {
-                                    printf("Codec width, height and framerate do not match for stream %d\n", i);
+                                    http_log("Codec width, height and framerate do not match for stream %d\n", i);
                                     matches = 0;
                                 }
                             } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
                                 if (CHECK_CODEC(sample_rate) ||
                                     CHECK_CODEC(channels) ||
                                     CHECK_CODEC(frame_size)) {
-                                    printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
+                                    http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
                                     matches = 0;
                                 }
                             } else {
-                                printf("Unknown codec type\n");
+                                http_log("Unknown codec type\n");
                                 matches = 0;
                             }
                         }
@@ -3479,17 +3485,17 @@ static void build_feed_streams(void)
                             break;
                     }
                 } else
-                    printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
+                    http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
                         feed->feed_filename, s->nb_streams, feed->nb_streams);
 
                 av_close_input_file(s);
             } else
-                printf("Deleting feed file '%s' as it appears to be corrupt\n",
+                http_log("Deleting feed file '%s' as it appears to be corrupt\n",
                         feed->feed_filename);
 
             if (!matches) {
                 if (feed->readonly) {
-                    printf("Unable to delete feed file '%s' as it is marked readonly\n",
+                    http_log("Unable to delete feed file '%s' as it is marked readonly\n",
                         feed->feed_filename);
                     exit(1);
                 }
@@ -3500,7 +3506,7 @@ static void build_feed_streams(void)
             AVFormatContext s1, *s = &s1;
 
             if (feed->readonly) {
-                printf("Unable to create feed file '%s' as it is marked readonly\n",
+                http_log("Unable to create feed file '%s' as it is marked readonly\n",
                     feed->feed_filename);
                 exit(1);
             }
@@ -3728,7 +3734,7 @@ static int opt_default(const char *opt, const char *arg,
     const AVOption *o  = NULL;
     const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
     if(o2)
-        o = av_set_string(avctx, opt, arg);
+        o = av_set_string2(avctx, opt, arg, 1);
     if(!o)
         return -1;
     return 0;
@@ -3882,15 +3888,6 @@ static int parse_ffconfig(const char *filename)
                         (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
                     inet_ntoa(my_http_addr.sin_addr),
                     ntohs(my_http_addr.sin_port), feed->filename);
-
-                if (ffserver_debug)
-                {
-                    int j;
-                    fprintf(stdout, "Launch commandline: ");
-                    for (j = 0; j <= i; j++)
-                        fprintf(stdout, "%s ", feed->child_argv[j]);
-                    fprintf(stdout, "\n");
-                }
             }
         } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
             if (feed) {
@@ -3986,25 +3983,25 @@ static int parse_ffconfig(const char *filename)
         } else if (!strcasecmp(cmd, "Format")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
-            if (!strcmp(arg, "status")) {
-                stream->stream_type = STREAM_TYPE_STATUS;
-                stream->fmt = NULL;
-            } else {
-                stream->stream_type = STREAM_TYPE_LIVE;
-                /* jpeg cannot be used here, so use single frame jpeg */
-                if (!strcmp(arg, "jpeg"))
-                    strcpy(arg, "mjpeg");
-                stream->fmt = guess_stream_format(arg, NULL, NULL);
-                if (!stream->fmt) {
-                    fprintf(stderr, "%s:%d: Unknown Format: %s\n",
-                            filename, line_num, arg);
-                    errors++;
+                if (!strcmp(arg, "status")) {
+                    stream->stream_type = STREAM_TYPE_STATUS;
+                    stream->fmt = NULL;
+                } else {
+                    stream->stream_type = STREAM_TYPE_LIVE;
+                    /* jpeg cannot be used here, so use single frame jpeg */
+                    if (!strcmp(arg, "jpeg"))
+                        strcpy(arg, "mjpeg");
+                    stream->fmt = guess_stream_format(arg, NULL, NULL);
+                    if (!stream->fmt) {
+                        fprintf(stderr, "%s:%d: Unknown Format: %s\n",
+                                filename, line_num, arg);
+                        errors++;
+                    }
+                }
+                if (stream->fmt) {
+                    audio_id = stream->fmt->audio_codec;
+                    video_id = stream->fmt->video_codec;
                 }
-            }
-            if (stream->fmt) {
-                audio_id = stream->fmt->audio_codec;
-                video_id = stream->fmt->video_codec;
-            }
             }
         } else if (!strcasecmp(cmd, "InputFormat")) {
             get_arg(arg, sizeof(arg), &p);
@@ -4485,6 +4482,15 @@ int main(int argc, char **argv)
         exit(1);
     }
 
+    /* open log file if needed */
+    if (logfilename[0] != '\0') {
+        if (!strcmp(logfilename, "-"))
+            logfile = stdout;
+        else
+            logfile = fopen(logfilename, "a");
+        av_log_set_callback(http_av_log);
+    }
+
     build_file_streams();
 
     build_feed_streams();
@@ -4505,7 +4511,6 @@ int main(int argc, char **argv)
         } else {
             /* child */
             setsid();
-            chdir("/");
             close(0);
             open("/dev/null", O_RDWR);
             if (strcmp(logfilename, "-") != 0) {
@@ -4520,14 +4525,8 @@ int main(int argc, char **argv)
     /* signal init */
     signal(SIGPIPE, SIG_IGN);
 
-    /* open log file if needed */
-    if (logfilename[0] != '\0') {
-        if (!strcmp(logfilename, "-"))
-            logfile = stderr;
-        else
-            logfile = fopen(logfilename, "a");
-        av_log_set_callback(http_av_log);
-    }
+    if (ffserver_daemon)
+        chdir("/");
 
     if (http_server() < 0) {
         http_log("Could not start server\n");