char protocol[16];
char method[16];
char url[128];
+ char clean_url[128*7];
int buffer_size;
uint8_t *buffer;
int is_packetized; /* if true, the stream is packetized */
uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
} HTTPContext;
-typedef struct FeedData {
- long long data_count;
- float avg_frame_size; /* frame size averaged over last frames with exponential mean */
-} FeedData;
-
static HTTPContext *first_http_ctx;
static FFServerConfig config = {
/* utils */
static size_t htmlencode (const char *src, char **dest);
static inline void cp_html_entity (char *buffer, const char *entity);
-static inline int check_codec_match(AVStream *ccf, AVStream *ccs, int stream);
+static inline int check_codec_match(LayeredAVStream *ccf, AVStream *ccs, int stream);
static const char *my_program_name;
static FILE *logfile = NULL;
+static void unlayer_stream(AVStream *st, LayeredAVStream *lst)
+{
+ avcodec_free_context(&st->codec);
+ avcodec_parameters_free(&st->codecpar);
+#define COPY(a) st->a = lst->a;
+ COPY(index)
+ COPY(id)
+ COPY(codec)
+ COPY(codecpar)
+ COPY(time_base)
+ COPY(pts_wrap_bits)
+ COPY(sample_aspect_ratio)
+ COPY(recommended_encoder_configuration)
+}
+
static inline void cp_html_entity (char *buffer, const char *entity) {
if (!buffer || !entity)
return;
return;
}
- pathname = av_strdup (my_program_name);
+ slash = strrchr(my_program_name, '/');
+ if (!slash) {
+ pathname = av_mallocz(sizeof("ffmpeg"));
+ } else {
+ pathname = av_mallocz(slash - my_program_name + sizeof("ffmpeg"));
+ if (pathname != NULL) {
+ memcpy(pathname, my_program_name, slash - my_program_name);
+ }
+ }
if (!pathname) {
http_log("Could not allocate memory for children cmd line\n");
return;
}
- /* replace "ffserver" with "ffmpeg" in the path of current
- * program. Ignore user provided path */
+ /* use "ffmpeg" in the path of current program. Ignore user provided path */
- slash = strrchr(pathname, '/');
- if (!slash)
- slash = pathname;
- else
- slash++;
- strcpy(slash, "ffmpeg");
+ strcat(pathname, "ffmpeg");
for (; feed; feed = feed->next) {
int i, stream_no;
const char *type = "unknown";
char parameters[64];
- AVStream *st;
+ LayeredAVStream *st;
AVCodec *codec;
stream_no = stream->nb_streams;
- avio_printf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>"
- "type<th>kbit/s<th align=left>codec<th align=left>"
+ avio_printf(pb, "<table><tr><th>Stream<th>"
+ "type<th>kbit/s<th>codec<th>"
"Parameters\n");
for (i = 0; i < stream_no; i++) {
abort();
}
- avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%"PRId64
+ avio_printf(pb, "<tr><td>%d<td>%s<td>%"PRId64
"<td>%s<td>%s\n",
i, type, (int64_t)st->codecpar->bit_rate/1000,
codec ? codec->name : "", parameters);
avio_printf(pb, "</table>\n");
}
+static void clean_html(char *clean, int clean_len, char *dirty)
+{
+ int i, o;
+
+ for (o = i = 0; o+10 < clean_len && dirty[i];) {
+ int len = strspn(dirty+i, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$-_.+!*(),?/ :;%");
+ if (len) {
+ if (o + len >= clean_len)
+ break;
+ memcpy(clean + o, dirty + i, len);
+ i += len;
+ o += len;
+ } else {
+ int c = dirty[i++];
+ switch (c) {
+ case '&': av_strlcat(clean+o, "&" , clean_len - o); break;
+ case '<': av_strlcat(clean+o, "<" , clean_len - o); break;
+ case '>': av_strlcat(clean+o, ">" , clean_len - o); break;
+ case '\'': av_strlcat(clean+o, "'" , clean_len - o); break;
+ case '\"': av_strlcat(clean+o, """ , clean_len - o); break;
+ default: av_strlcat(clean+o, "☹", clean_len - o); break;
+ }
+ o += strlen(clean+o);
+ }
+ }
+ clean[o] = 0;
+}
+
static void compute_status(HTTPContext *c)
{
HTTPContext *c1;
avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
/* format status */
avio_printf(pb, "<h2>Available Streams</h2>\n");
- avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
- avio_printf(pb, "<tr><th valign=top>Path<th align=left>Served<br>Conns<th><br>bytes<th valign=top>Format<th>Bit rate<br>kbit/s<th align=left>Video<br>kbit/s<th><br>Codec<th align=left>Audio<br>kbit/s<th><br>Codec<th align=left valign=top>Feed\n");
+ avio_printf(pb, "<table>\n");
+ avio_printf(pb, "<tr><th>Path<th>Served<br>Conns<th><br>bytes<th>Format<th>Bit rate<br>kbit/s<th>Video<br>kbit/s<th><br>Codec<th>Audio<br>kbit/s<th><br>Codec<th>Feed\n");
stream = config.first_stream;
while (stream) {
char sfilename[1024];
avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
sfilename, stream->filename);
- avio_printf(pb, "<td align=right> %d <td align=right> ",
+ avio_printf(pb, "<td> %d <td> ",
stream->conns_served);
- fmt_bytecount(pb, stream->bytes_served);
+ // TODO: Investigate if we can make http bitexact so it always produces the same count of bytes
+ if (!config.bitexact)
+ fmt_bytecount(pb, stream->bytes_served);
switch(stream->stream_type) {
case STREAM_TYPE_LIVE: {
const char *video_codec_name_extra = "";
for(i=0;i<stream->nb_streams;i++) {
- AVStream *st = stream->streams[i];
+ LayeredAVStream *st = stream->streams[i];
AVCodec *codec = avcodec_find_encoder(st->codecpar->codec_id);
switch(st->codecpar->codec_type) {
}
}
- avio_printf(pb, "<td align=center> %s <td align=right> %d "
- "<td align=right> %d <td> %s %s <td align=right> "
+ avio_printf(pb, "<td> %s <td> %d <td> %d <td> %s %s <td> "
"%d <td> %s %s",
stream->fmt->name, stream->bandwidth,
video_bit_rate / 1000, video_codec_name,
}
break;
default:
- avio_printf(pb, "<td align=center> - <td align=right> - "
- "<td align=right> - <td><td align=right> - <td>\n");
+ avio_printf(pb, "<td> - <td> - "
+ "<td> - <td><td> - <td>\n");
break;
}
stream = stream->next;
current_bandwidth, config.max_bandwidth);
avio_printf(pb, "<table>\n");
- avio_printf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target "
+ avio_printf(pb, "<tr><th>#<th>File<th>IP<th>URL<th>Proto<th>State<th>Target "
"bit/s<th>Actual bit/s<th>Bytes transferred\n");
c1 = first_http_ctx;
i = 0;
i++;
p = inet_ntoa(c1->from_addr.sin_addr);
- avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s"
- "<td align=right>",
+ clean_html(c1->clean_url, sizeof(c1->clean_url), c1->url);
+ avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td>%s"
+ "<td>",
i, c1->stream ? c1->stream->filename : "",
- c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "", p,
+ c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
+ p,
+ c1->clean_url,
c1->protocol, http_state[c1->state]);
fmt_bytecount(pb, bitrate);
- avio_printf(pb, "<td align=right>");
+ avio_printf(pb, "<td>");
fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
- avio_printf(pb, "<td align=right>");
+ avio_printf(pb, "<td>");
fmt_bytecount(pb, c1->data_count);
avio_printf(pb, "\n");
c1 = c1->next;
}
avio_printf(pb, "</table>\n");
- /* date */
- ti = time(NULL);
- p = ctime(&ti);
- avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
+ if (!config.bitexact) {
+ /* date */
+ ti = time(NULL);
+ p = ctime(&ti);
+ avio_printf(pb, "<hr>Generated at %s", p);
+ }
avio_printf(pb, "</body>\n</html>\n");
len = avio_close_dyn_buf(pb, &c->pb_buffer);
c->pts_stream_index = 0;
for(i=0;i<c->stream->nb_streams;i++) {
if (c->pts_stream_index == 0 &&
- c->stream->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
c->pts_stream_index = i;
}
}
return AVERROR(ENOMEM);
c->pfmt_ctx = ctx;
av_dict_copy(&(c->pfmt_ctx->metadata), c->stream->metadata, 0);
- c->pfmt_ctx->streams = av_mallocz_array(c->stream->nb_streams,
- sizeof(AVStream *));
- if (!c->pfmt_ctx->streams)
- return AVERROR(ENOMEM);
for(i=0;i<c->stream->nb_streams;i++) {
- AVStream *src;
- c->pfmt_ctx->streams[i] = av_mallocz(sizeof(AVStream));
+ LayeredAVStream *src;
+ AVStream *st = avformat_new_stream(c->pfmt_ctx, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
/* if file or feed, then just take streams from FFServerStream
* struct */
else
src = c->stream->feed->streams[c->stream->feed_streams[i]];
- *(c->pfmt_ctx->streams[i]) = *src;
- c->pfmt_ctx->streams[i]->priv_data = 0;
- /* XXX: should be done in AVStream, not in codec */
- c->pfmt_ctx->streams[i]->codec->frame_number = 0;
+ unlayer_stream(c->pfmt_ctx->streams[i], src); //TODO we no longer copy st->internal, does this matter?
+ av_assert0(!c->pfmt_ctx->streams[i]->priv_data);
+
+ if (src->codec->flags & AV_CODEC_FLAG_BITEXACT)
+ c->pfmt_ctx->flags |= AVFMT_FLAG_BITEXACT;
}
/* set output format parameters */
c->pfmt_ctx->oformat = c->stream->fmt;
- c->pfmt_ctx->nb_streams = c->stream->nb_streams;
+ av_assert0(c->pfmt_ctx->nb_streams == c->stream->nb_streams);
c->got_key_frame = 0;
AVStream *st = c->fmt_in->streams[source_index];
pkt.stream_index = i;
if (pkt.flags & AV_PKT_FLAG_KEY &&
- (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
+ (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
c->stream->nb_streams == 1))
c->got_key_frame = 1;
if (!c->stream->send_on_key || c->got_key_frame)
}
}
} else {
- AVCodecContext *codec;
AVStream *ist, *ost;
send_it:
ist = c->fmt_in->streams[source_index];
av_packet_unref(&pkt);
break;
}
- codec = ctx->streams[0]->codec;
/* only one stream per RTP connection */
pkt.stream_index = 0;
} else {
ctx = c->pfmt_ctx;
/* Fudge here */
- codec = ctx->streams[pkt.stream_index]->codec;
}
if (c->is_packetized) {
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
- codec->frame_number++;
if (len == 0) {
av_packet_unref(&pkt);
goto redo;
} else if (c->buffer_ptr - c->buffer >= 2 &&
!memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
c->chunk_size = strtol(c->buffer, 0, 16);
- if (c->chunk_size == 0) // end of stream
+ if (c->chunk_size <= 0) { // end of stream or invalid chunk size
+ c->chunk_size = 0;
goto fail;
+ }
c->buffer_ptr = c->buffer;
break;
} else if (++loop_run > 10)
/* end of connection : close it */
goto fail;
else {
+ av_assert0(len <= c->chunk_size);
c->chunk_size -= len;
c->buffer_ptr += len;
c->data_count += len;
}
for (i = 0; i < s->nb_streams; i++) {
- AVStream *fst = feed->streams[i];
+ LayeredAVStream *fst = feed->streams[i];
AVStream *st = s->streams[i];
- avcodec_copy_context(fst->codec, st->codec);
+ avcodec_parameters_to_context(fst->codec, st->codecpar);
+ avcodec_parameters_from_context(fst->codecpar, fst->codec);
}
avformat_close_input(&s);
struct in_addr my_ip)
{
AVFormatContext *avc;
- AVStream *avs = NULL;
AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
AVDictionaryEntry *entry = av_dict_get(stream->metadata, "title", NULL, 0);
int i;
avc->oformat = rtp_format;
av_dict_set(&avc->metadata, "title",
entry ? entry->value : "No Title", 0);
- avc->nb_streams = stream->nb_streams;
if (stream->is_multicast) {
snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
inet_ntoa(stream->multicast_ip),
} else
snprintf(avc->filename, 1024, "rtp://0.0.0.0");
- avc->streams = av_malloc_array(avc->nb_streams, sizeof(*avc->streams));
- if (!avc->streams)
- goto sdp_done;
-
- avs = av_malloc_array(avc->nb_streams, sizeof(*avs));
- if (!avs)
- goto sdp_done;
-
for(i = 0; i < stream->nb_streams; i++) {
- avc->streams[i] = &avs[i];
- avc->streams[i]->codec = stream->streams[i]->codec;
+ AVStream *st = avformat_new_stream(avc, NULL);
+ if (!st)
+ goto sdp_done;
avcodec_parameters_from_context(stream->streams[i]->codecpar, stream->streams[i]->codec);
- avc->streams[i]->codecpar = stream->streams[i]->codecpar;
+ unlayer_stream(st, stream->streams[i]);
}
#define PBUFFER_SIZE 2048
*pbuffer = av_mallocz(PBUFFER_SIZE);
av_freep(&avc->streams);
av_dict_free(&avc->metadata);
av_free(avc);
- av_free(avs);
return *pbuffer ? strlen(*pbuffer) : AVERROR(ENOMEM);
}
if (!st)
goto fail;
- av_freep(&st->codec);
- av_freep(&st->info);
st_internal = st->internal;
if (!c->stream->feed ||
c->stream->feed == c->stream)
- memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
+ unlayer_stream(st, c->stream->streams[stream_index]);
else
- memcpy(st,
- c->stream->feed->streams[c->stream->feed_streams[stream_index]],
- sizeof(AVStream));
- st->priv_data = NULL;
- st->internal = st_internal;
+ unlayer_stream(st,
+ c->stream->feed->streams[c->stream->feed_streams[stream_index]]);
+ av_assert0(st->priv_data == NULL);
+ av_assert0(st->internal == st_internal);
/* build destination RTP address */
ipaddr = inet_ntoa(dest_addr->sin_addr);
/* ffserver initialization */
/* FIXME: This code should use avformat_new_stream() */
-static AVStream *add_av_stream1(FFServerStream *stream,
+static LayeredAVStream *add_av_stream1(FFServerStream *stream,
AVCodecContext *codec, int copy)
{
- AVStream *fst;
+ LayeredAVStream *fst;
if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
return NULL;
- fst = av_mallocz(sizeof(AVStream));
+ fst = av_mallocz(sizeof(*fst));
if (!fst)
return NULL;
if (copy) {
*/
fst->codec = codec;
- fst->priv_data = av_mallocz(sizeof(FeedData));
- fst->internal = av_mallocz(sizeof(*fst->internal));
- fst->internal->avctx = avcodec_alloc_context3(NULL);
+ //NOTE we previously allocated internal & internal->avctx, these seemed uneeded though
fst->codecpar = avcodec_parameters_alloc();
fst->index = stream->nb_streams;
- avpriv_set_pts_info(fst, 33, 1, 90000);
+ fst->time_base = codec->time_base;
+ fst->pts_wrap_bits = 33;
fst->sample_aspect_ratio = codec->sample_aspect_ratio;
stream->streams[stream->nb_streams++] = fst;
return fst;
}
/* return the stream number in the feed */
-static int add_av_stream(FFServerStream *feed, AVStream *st)
+static int add_av_stream(FFServerStream *feed, LayeredAVStream *st)
{
- AVStream *fst;
+ LayeredAVStream *fst;
AVCodecContext *av, *av1;
int i;
fst = add_av_stream1(feed, av, 0);
if (!fst)
return -1;
- if (av_stream_get_recommended_encoder_configuration(st))
- av_stream_set_recommended_encoder_configuration(fst,
- av_strdup(av_stream_get_recommended_encoder_configuration(st)));
+ if (st->recommended_encoder_configuration)
+ fst->recommended_encoder_configuration =
+ av_strdup(st->recommended_encoder_configuration);
return feed->nb_streams - 1;
}
}
}
-/* specific MPEG4 handling : we extract the raw parameters */
-static void extract_mpeg4_header(AVFormatContext *infile)
-{
- int mpeg4_count, i, size;
- AVPacket pkt;
- AVStream *st;
- const uint8_t *p;
-
- infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;
-
- mpeg4_count = 0;
- for(i=0;i<infile->nb_streams;i++) {
- st = infile->streams[i];
- if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
- st->codec->extradata_size == 0) {
- mpeg4_count++;
- }
- }
- if (!mpeg4_count)
- return;
-
- printf("MPEG4 without extra data: trying to find header in %s\n",
- infile->filename);
- while (mpeg4_count > 0) {
- if (av_read_frame(infile, &pkt) < 0)
- break;
- st = infile->streams[pkt.stream_index];
- if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
- st->codec->extradata_size == 0) {
- av_freep(&st->codec->extradata);
- /* fill extradata with the header */
- /* XXX: we make hard suppositions here ! */
- p = pkt.data;
- while (p < pkt.data + pkt.size - 4) {
- /* stop when vop header is found */
- if (p[0] == 0x00 && p[1] == 0x00 &&
- p[2] == 0x01 && p[3] == 0xb6) {
- size = p - pkt.data;
- st->codec->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
- st->codec->extradata_size = size;
- memcpy(st->codec->extradata, pkt.data, size);
- break;
- }
- p++;
- }
- mpeg4_count--;
- }
- av_packet_unref(&pkt);
- }
-}
-
/* compute the needed AVStream for each file */
static void build_file_streams(void)
{
avformat_close_input(&infile);
goto fail;
}
- extract_mpeg4_header(infile);
for(i=0;i<infile->nb_streams;i++)
add_av_stream1(stream, infile->streams[i]->codec, 1);
}
static inline
-int check_codec_match(AVStream *ccf, AVStream *ccs, int stream)
+int check_codec_match(LayeredAVStream *ccf, AVStream *ccs, int stream)
{
int matches = 1;
matches = 1;
for(i=0;i<s->nb_streams;i++) {
- AVStream *sf, *ss;
+ AVStream *ss;
+ LayeredAVStream *sf;
sf = feed->streams[i];
ss = s->streams[i];
goto bail;
}
s->oformat = feed->fmt;
- s->nb_streams = feed->nb_streams;
- s->streams = feed->streams;
+ for (i = 0; i<feed->nb_streams; i++) {
+ AVStream *st = avformat_new_stream(s, NULL); // FIXME free this
+ if (!st) {
+ http_log("Failed to allocate stream\n");
+ goto bail;
+ }
+ unlayer_stream(st, feed->streams[i]);
+ }
if (avformat_write_header(s, NULL) < 0) {
http_log("Container doesn't support the required parameters\n");
avio_closep(&s->pb);
for(stream = config.first_stream; stream; stream = stream->next) {
bandwidth = 0;
for(i=0;i<stream->nb_streams;i++) {
- AVStream *st = stream->streams[i];
+ LayeredAVStream *st = stream->streams[i];
switch(st->codec->codec_type) {
case AVMEDIA_TYPE_AUDIO:
case AVMEDIA_TYPE_VIDEO: