From 4cc3467e7abfea7e8d03b6af511f7719038a5a98 Mon Sep 17 00:00:00 2001 From: Chris Evans Date: Wed, 29 Jun 2011 15:44:40 -0700 Subject: [PATCH 01/13] oggdec: prevent heap corruption. Specifically crafted samples can reinit ogg->streams[] while reading samples, and thus we should not cache old pointers since these may no longer be valid. Signed-off-by: Ronald S. Bultje --- libavformat/oggdec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index e33de7d978..3d03a5f974 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -592,15 +592,15 @@ static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index, int64_t *pos_arg, int64_t pos_limit) { struct ogg *ogg = s->priv_data; - struct ogg_stream *os = ogg->streams + stream_index; AVIOContext *bc = s->pb; int64_t pts = AV_NOPTS_VALUE; - int i; + int i = -1; avio_seek(bc, *pos_arg, SEEK_SET); ogg_reset(ogg); while (avio_tell(bc) < pos_limit && !ogg_packet(s, &i, NULL, NULL, pos_arg)) { if (i == stream_index) { + struct ogg_stream *os = ogg->streams + stream_index; pts = ogg_calc_pts(s, i, NULL); if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) pts = AV_NOPTS_VALUE; @@ -626,6 +626,7 @@ static int ogg_read_seek(AVFormatContext *s, int stream_index, os->keyframe_seek = 1; ret = av_seek_frame_binary(s, stream_index, timestamp, flags); + os = ogg->streams + stream_index; if (ret < 0) os->keyframe_seek = 0; return ret; From 93ade976c8e0af5cfe4a0d2f5a32875efe73e2d9 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Fri, 24 Jun 2011 21:34:42 +0200 Subject: [PATCH 02/13] matroskaenc: make SSA default subtitle codec. It's a better format and we have an encoder for it, so it doesn't fail like CODEC_ID_TEXT. --- libavformat/matroskaenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 169e8e44da..d0e3c171eb 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -1205,7 +1205,7 @@ AVOutputFormat ff_matroska_muxer = { mkv_write_trailer, .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS, .codec_tag = (const AVCodecTag* const []){ff_codec_bmp_tags, ff_codec_wav_tags, 0}, - .subtitle_codec = CODEC_ID_TEXT, + .subtitle_codec = CODEC_ID_SSA, }; #endif From 1cede1d0112b5b26e6d023846729662b7cf1853f Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sat, 25 Jun 2011 07:43:49 +0200 Subject: [PATCH 03/13] ffmpeg: factor common code from new_a/v/s/d_stream to new_output_stream() --- ffmpeg.c | 80 ++++++++++++++++++++------------------------------------ 1 file changed, 28 insertions(+), 52 deletions(-) diff --git a/ffmpeg.c b/ffmpeg.c index fe8b3446b8..91cfb80b14 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -640,10 +640,16 @@ static void choose_pixel_fmt(AVStream *st, AVCodec *codec) } } -static OutputStream *new_output_stream(AVFormatContext *oc, int file_idx) +static OutputStream *new_output_stream(AVFormatContext *oc, int file_idx, AVCodec *codec) { - int idx = oc->nb_streams - 1; OutputStream *ost; + AVStream *st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0); + int idx = oc->nb_streams - 1; + + if (!st) { + av_log(NULL, AV_LOG_ERROR, "Could not alloc stream.\n"); + ffmpeg_exit(1); + } output_streams_for_file[file_idx] = grow_array(output_streams_for_file[file_idx], @@ -658,6 +664,10 @@ static OutputStream *new_output_stream(AVFormatContext *oc, int file_idx) } ost->file_index = file_idx; ost->index = idx; + ost->st = st; + ost->enc = codec; + + avcodec_get_context_defaults3(st->codec, codec); ost->sws_flags = av_get_int(sws_opts, "sws_flags", NULL); return ost; @@ -673,27 +683,20 @@ static int read_ffserver_streams(AVFormatContext *s, const char *filename) if (err < 0) return err; /* copy stream format */ - s->nb_streams = 0; - s->streams = av_mallocz(sizeof(AVStream *) * ic->nb_streams); for(i=0;inb_streams;i++) { AVStream *st; + OutputStream *ost; AVCodec *codec; - s->nb_streams++; + codec = avcodec_find_encoder(ic->streams[i]->codec->codec_id); + ost = new_output_stream(s, nb_output_files, codec); + st = ost->st; // FIXME: a more elegant solution is needed - st = av_mallocz(sizeof(AVStream)); memcpy(st, ic->streams[i], sizeof(AVStream)); st->info = NULL; - st->codec = avcodec_alloc_context(); - if (!st->codec) { - print_error(filename, AVERROR(ENOMEM)); - ffmpeg_exit(1); - } avcodec_copy_context(st->codec, ic->streams[i]->codec); - s->streams[i] = st; - codec = avcodec_find_encoder(st->codec->codec_id); if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { if (audio_stream_copy) { st->stream_copy = 1; @@ -708,8 +711,6 @@ static int read_ffserver_streams(AVFormatContext *s, const char *filename) if(st->codec->flags & CODEC_FLAG_BITEXACT) nopts = 1; - - new_output_stream(s, nb_output_files); } if (!nopts) @@ -2003,7 +2004,6 @@ static int transcode(AVFormatContext **output_files, for(i=0;inb_streams;i++,n++) { int found; ost = ost_table[n] = output_streams_for_file[k][i]; - ost->st = os->streams[i]; if (nb_stream_maps > 0) { ost->source_index = input_files[stream_maps[n].file_index].ist_index + stream_maps[n].stream_index; @@ -3428,24 +3428,20 @@ static void new_video_stream(AVFormatContext *oc, int file_idx) enum CodecID codec_id = CODEC_ID_NONE; AVCodec *codec= NULL; - st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - ffmpeg_exit(1); - } - ost = new_output_stream(oc, file_idx); - if(!video_stream_copy){ if (video_codec_name) { codec_id = find_codec_or_die(video_codec_name, AVMEDIA_TYPE_VIDEO, 1, avcodec_opts[AVMEDIA_TYPE_VIDEO]->strict_std_compliance); codec = avcodec_find_encoder_by_name(video_codec_name); - ost->enc = codec; } else { codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_VIDEO); codec = avcodec_find_encoder(codec_id); } + } + ost = new_output_stream(oc, file_idx, codec); + st = ost->st; + if (!video_stream_copy) { ost->frame_aspect_ratio = frame_aspect_ratio; frame_aspect_ratio = 0; #if CONFIG_AVFILTER @@ -3454,7 +3450,6 @@ static void new_video_stream(AVFormatContext *oc, int file_idx) #endif } - avcodec_get_context_defaults3(st->codec, codec); ost->bitstream_filters = video_bitstream_filters; video_bitstream_filters= NULL; @@ -3567,26 +3562,18 @@ static void new_audio_stream(AVFormatContext *oc, int file_idx) AVCodecContext *audio_enc; enum CodecID codec_id = CODEC_ID_NONE; - st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - ffmpeg_exit(1); - } - ost = new_output_stream(oc, file_idx); - if(!audio_stream_copy){ if (audio_codec_name) { codec_id = find_codec_or_die(audio_codec_name, AVMEDIA_TYPE_AUDIO, 1, avcodec_opts[AVMEDIA_TYPE_AUDIO]->strict_std_compliance); codec = avcodec_find_encoder_by_name(audio_codec_name); - ost->enc = codec; } else { codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_AUDIO); codec = avcodec_find_encoder(codec_id); } } - - avcodec_get_context_defaults3(st->codec, codec); + ost = new_output_stream(oc, file_idx, codec); + st = ost->st; ost->bitstream_filters = audio_bitstream_filters; audio_bitstream_filters= NULL; @@ -3635,21 +3622,16 @@ static void new_audio_stream(AVFormatContext *oc, int file_idx) static void new_data_stream(AVFormatContext *oc, int file_idx) { AVStream *st; - AVCodec *codec=NULL; + OutputStream *ost; AVCodecContext *data_enc; - st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - ffmpeg_exit(1); - } - new_output_stream(oc, file_idx); + ost = new_output_stream(oc, file_idx, NULL); + st = ost->st; data_enc = st->codec; if (!data_stream_copy) { fprintf(stderr, "Data stream encoding not supported yet (only streamcopy)\n"); ffmpeg_exit(1); } - avcodec_get_context_defaults3(st->codec, codec); data_enc->codec_type = AVMEDIA_TYPE_DATA; @@ -3677,25 +3659,19 @@ static void new_subtitle_stream(AVFormatContext *oc, int file_idx) AVCodecContext *subtitle_enc; enum CodecID codec_id = CODEC_ID_NONE; - st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - ffmpeg_exit(1); - } - ost = new_output_stream(oc, file_idx); - subtitle_enc = st->codec; if(!subtitle_stream_copy){ if (subtitle_codec_name) { codec_id = find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 1, avcodec_opts[AVMEDIA_TYPE_SUBTITLE]->strict_std_compliance); codec = avcodec_find_encoder_by_name(subtitle_codec_name); - ost->enc = codec; } else { codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_SUBTITLE); codec = avcodec_find_encoder(codec_id); } } - avcodec_get_context_defaults3(st->codec, codec); + ost = new_output_stream(oc, file_idx, codec); + st = ost->st; + subtitle_enc = st->codec; ost->bitstream_filters = subtitle_bitstream_filters; subtitle_bitstream_filters= NULL; From 87c739a395ff2951058d719687cd06b0e89420bd Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 15 Jun 2011 08:00:03 +0200 Subject: [PATCH 04/13] ffmpeg: don't abuse a global for passing channel layout from input to output It's broken with multiple files or audio streams. --- ffmpeg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ffmpeg.c b/ffmpeg.c index 91cfb80b14..8035848e07 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -161,7 +161,6 @@ static char *vfilters = NULL; static int intra_only = 0; static int audio_sample_rate = 0; -static int64_t channel_layout = 0; #define QSCALE_NONE -99999 static float audio_qscale = QSCALE_NONE; static int audio_disable = 0; @@ -2183,6 +2182,7 @@ static int transcode(AVFormatContext **output_files, codec->time_base = (AVRational){1, codec->sample_rate}; if (!codec->channels) codec->channels = icodec->channels; + codec->channel_layout = icodec->channel_layout; if (av_get_channel_layout_nb_channels(codec->channel_layout) != codec->channels) codec->channel_layout = 0; ost->audio_resample = codec->sample_rate != icodec->sample_rate || audio_sync_method > 1; @@ -3306,7 +3306,6 @@ static int opt_input_file(const char *opt, const char *filename) case AVMEDIA_TYPE_AUDIO: ist->dec = avcodec_find_decoder_by_name(audio_codec_name); set_context_opts(dec, avcodec_opts[AVMEDIA_TYPE_AUDIO], AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM, ist->dec); - channel_layout = dec->channel_layout; audio_sample_fmt = dec->sample_fmt; if(audio_disable) st->discard= AVDISCARD_ALL; @@ -3605,7 +3604,6 @@ static void new_audio_stream(AVFormatContext *oc, int file_idx) audio_enc->sample_fmt = audio_sample_fmt; if (audio_sample_rate) audio_enc->sample_rate = audio_sample_rate; - audio_enc->channel_layout = channel_layout; choose_sample_fmt(st, codec); } if (audio_language) { From 6fbf2485ad5b1d52f55b229acbf2a7de2b752283 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 15 Jun 2011 08:00:03 +0200 Subject: [PATCH 05/13] ffmpeg: don't abuse a global for passing sample format from input to output It's broken with multiple files or audio streams. --- ffmpeg.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ffmpeg.c b/ffmpeg.c index 8035848e07..b140b0d395 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -2180,6 +2180,9 @@ static int transcode(AVFormatContext **output_files, } choose_sample_rate(ost->st, ost->enc); codec->time_base = (AVRational){1, codec->sample_rate}; + if (codec->sample_fmt == AV_SAMPLE_FMT_NONE) + codec->sample_fmt = icodec->sample_fmt; + choose_sample_fmt(ost->st, ost->enc); if (!codec->channels) codec->channels = icodec->channels; codec->channel_layout = icodec->channel_layout; @@ -3306,7 +3309,6 @@ static int opt_input_file(const char *opt, const char *filename) case AVMEDIA_TYPE_AUDIO: ist->dec = avcodec_find_decoder_by_name(audio_codec_name); set_context_opts(dec, avcodec_opts[AVMEDIA_TYPE_AUDIO], AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM, ist->dec); - audio_sample_fmt = dec->sample_fmt; if(audio_disable) st->discard= AVDISCARD_ALL; break; @@ -3367,6 +3369,7 @@ static int opt_input_file(const char *opt, const char *filename) frame_width = 0; audio_sample_rate = 0; audio_channels = 0; + audio_sample_fmt = AV_SAMPLE_FMT_NONE; av_freep(&video_codec_name); av_freep(&audio_codec_name); @@ -3601,10 +3604,10 @@ static void new_audio_stream(AVFormatContext *oc, int file_idx) } if (audio_channels) audio_enc->channels = audio_channels; - audio_enc->sample_fmt = audio_sample_fmt; + if (audio_sample_fmt != AV_SAMPLE_FMT_NONE) + audio_enc->sample_fmt = audio_sample_fmt; if (audio_sample_rate) audio_enc->sample_rate = audio_sample_rate; - choose_sample_fmt(st, codec); } if (audio_language) { av_dict_set(&st->metadata, "language", audio_language, 0); @@ -3875,6 +3878,7 @@ static void opt_output_file(const char *filename) frame_height = 0; audio_sample_rate = 0; audio_channels = 0; + audio_sample_fmt = AV_SAMPLE_FMT_NONE; av_freep(&forced_key_frames); uninit_opts(); From b12c2592525c3d8e12265a3d923a945d6f699a5b Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Thu, 7 Jul 2011 08:54:07 +0200 Subject: [PATCH 06/13] ffmpeg: merge input_files_ts_scale into InputStream. --- ffmpeg.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ffmpeg.c b/ffmpeg.c index b140b0d395..1af1d0bdd1 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -107,8 +107,8 @@ static const OptionDef options[]; #define FFM_PACKET_SIZE 4096 //XXX a duplicate of the line in ffm.h static const char *last_asked_format = NULL; -static double *input_files_ts_scale[MAX_FILES] = {NULL}; -static int nb_input_files_ts_scale[MAX_FILES] = {0}; +static double *ts_scale; +static int nb_ts_scale; static AVFormatContext *output_files[MAX_FILES]; static AVDictionary *output_opts[MAX_FILES]; @@ -312,6 +312,7 @@ typedef struct InputStream { is not defined */ int64_t pts; /* current pts */ PtsCorrectionContext pts_ctx; + double ts_scale; int is_start; /* is 1 at the start and after a discontinuity */ int showed_multi_packet_warning; int is_past_recording_time; @@ -461,7 +462,6 @@ static int ffmpeg_exit(int ret) } for(i=0;ifile_index].ts_offset, AV_TIME_BASE_Q, ist->st->time_base); - if (pkt.stream_index < nb_input_files_ts_scale[file_index] - && input_files_ts_scale[file_index][pkt.stream_index]){ + if (ist->ts_scale) { if(pkt.pts != AV_NOPTS_VALUE) - pkt.pts *= input_files_ts_scale[file_index][pkt.stream_index]; + pkt.pts *= ist->ts_scale; if(pkt.dts != AV_NOPTS_VALUE) - pkt.dts *= input_files_ts_scale[file_index][pkt.stream_index]; + pkt.dts *= ist->ts_scale; } // fprintf(stderr, "next:%"PRId64" dts:%"PRId64" off:%"PRId64" %d\n", ist->next_pts, pkt.dts, input_files[ist->file_index].ts_offset, ist->st->codec->codec_type); @@ -3091,8 +3090,8 @@ static int opt_input_ts_scale(const char *opt, const char *arg) if(stream >= MAX_STREAMS) ffmpeg_exit(1); - input_files_ts_scale[nb_input_files] = grow_array(input_files_ts_scale[nb_input_files], sizeof(*input_files_ts_scale[nb_input_files]), &nb_input_files_ts_scale[nb_input_files], stream + 1); - input_files_ts_scale[nb_input_files][stream]= scale; + ts_scale = grow_array(ts_scale, sizeof(*ts_scale), &nb_ts_scale, stream + 1); + ts_scale[stream] = scale; return 0; } @@ -3305,6 +3304,9 @@ static int opt_input_file(const char *opt, const char *filename) ist->file_index = nb_input_files; ist->discard = 1; + if (i < nb_ts_scale) + ist->ts_scale = ts_scale[i]; + switch (dec->codec_type) { case AVMEDIA_TYPE_AUDIO: ist->dec = avcodec_find_decoder_by_name(audio_codec_name); @@ -3370,6 +3372,8 @@ static int opt_input_file(const char *opt, const char *filename) audio_sample_rate = 0; audio_channels = 0; audio_sample_fmt = AV_SAMPLE_FMT_NONE; + av_freep(&ts_scale); + nb_ts_scale = 0; av_freep(&video_codec_name); av_freep(&audio_codec_name); From 5f847bf61dca1fd1a2f65a2f56c9a99d1cb716ab Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Thu, 7 Jul 2011 11:25:03 +0200 Subject: [PATCH 07/13] lavf: deprecate AVFormatContext.timestamp It's replaced by 'creation_time' metadata tag. --- ffmpeg.c | 15 ++++++++------- libavformat/avformat.h | 7 ++++++- libavformat/dvenc.c | 14 ++++++++++++-- libavformat/gxfenc.c | 18 ++++++++++++++++-- libavformat/movenc.c | 14 +++++++++++++- libavformat/mxfenc.c | 14 +++++++++++++- libavformat/version.h | 3 +++ 7 files changed, 71 insertions(+), 14 deletions(-) diff --git a/ffmpeg.c b/ffmpeg.c index 1af1d0bdd1..a0f6abc87d 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -183,7 +183,6 @@ static float mux_max_delay= 0.7; static int64_t recording_time = INT64_MAX; static int64_t start_time = 0; -static int64_t recording_timestamp = 0; static int64_t input_ts_offset = 0; static int file_overwrite = 0; static AVDictionary *metadata; @@ -712,9 +711,6 @@ static int read_ffserver_streams(AVFormatContext *s, const char *filename) nopts = 1; } - if (!nopts) - s->timestamp = av_gettime(); - av_close_input_file(ic); return 0; } @@ -3109,7 +3105,14 @@ static int opt_start_time(const char *opt, const char *arg) static int opt_recording_timestamp(const char *opt, const char *arg) { - recording_timestamp = parse_time_or_die(opt, arg, 0) / 1000000; + char buf[128]; + int64_t recording_timestamp = parse_time_or_die(opt, arg, 0) / 1E6; + struct tm time = *gmtime((time_t*)&recording_timestamp); + strftime(buf, sizeof(buf), "creation_time=%FT%T%z", &time); + opt_metadata("metadata", buf); + + av_log(NULL, AV_LOG_WARNING, "%s is deprecated, set the 'creation_time' metadata " + "tag instead.\n", opt); return 0; } @@ -3823,8 +3826,6 @@ static void opt_output_file(const char *filename) if (use_subtitle) new_subtitle_stream(oc, nb_output_files); if (use_data) new_data_stream(oc, nb_output_files); - oc->timestamp = recording_timestamp; - av_dict_copy(&oc->metadata, metadata, 0); av_dict_free(&metadata); } diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 6e861de262..38bb86592e 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -674,7 +674,12 @@ typedef struct AVFormatContext { AVStream **streams; char filename[1024]; /**< input or output filename */ /* stream info */ - int64_t timestamp; +#if FF_API_TIMESTAMP + /** + * @deprecated use 'creation_time' metadata tag instead + */ + attribute_deprecated int64_t timestamp; +#endif int ctx_flags; /**< Format-specific flags, see AVFMTCTX_xx */ /* private data for pts handling (do not modify directly). */ diff --git a/libavformat/dvenc.c b/libavformat/dvenc.c index 504e3ee26f..1e59bd9cc7 100644 --- a/libavformat/dvenc.c +++ b/libavformat/dvenc.c @@ -43,7 +43,7 @@ struct DVMuxContext { AVStream *ast[2]; /* stereo audio streams */ AVFifoBuffer *audio_data[2]; /* FIFO for storing excessive amounts of PCM */ int frames; /* current frame number */ - time_t start_time; /* recording start time */ + int64_t start_time; /* recording start time */ int has_audio; /* frame under contruction has audio */ int has_video; /* frame under contruction has video */ uint8_t frame_buf[DV_MAX_FRAME_SIZE]; /* frame under contruction */ @@ -290,6 +290,7 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) { DVMuxContext *c = s->priv_data; AVStream *vst = NULL; + AVDictionaryEntry *t; int i; /* we support at most 1 video and 2 audio streams */ @@ -337,7 +338,16 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) c->frames = 0; c->has_audio = 0; c->has_video = 0; - c->start_time = (time_t)s->timestamp; +#if FF_API_TIMESTAMP + if (s->timestamp) + c->start_time = s->timestamp; + else +#endif + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) { + struct tm time = {0}; + strptime(t->value, "%Y - %m - %dT%T", &time); + c->start_time = mktime(&time); + } for (i=0; i < c->n_ast; i++) { if (c->ast[i] && !(c->audio_data[i]=av_fifo_alloc(100*AVCODEC_MAX_AUDIO_FRAME_SIZE))) { diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c index 5a3ff39ab2..2d4136ab86 100644 --- a/libavformat/gxfenc.c +++ b/libavformat/gxfenc.c @@ -394,6 +394,20 @@ static int gxf_write_umf_material_description(AVFormatContext *s) GXFContext *gxf = s->priv_data; AVIOContext *pb = s->pb; int timecode_base = gxf->time_base.den == 60000 ? 60 : 50; + int64_t timestamp = 0; + AVDictionaryEntry *t; + +#if FF_API_TIMESTAMP + if (s->timestamp) + timestamp = s->timestamp; + else +#endif + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) { + struct tm time = {0}; + strptime(t->value, "%Y - %m - %dT%T", &time); + timestamp = mktime(&time); + } + // XXX drop frame uint32_t timecode = @@ -409,8 +423,8 @@ static int gxf_write_umf_material_description(AVFormatContext *s) avio_wl32(pb, gxf->nb_fields); /* mark out */ avio_wl32(pb, 0); /* timecode mark in */ avio_wl32(pb, timecode); /* timecode mark out */ - avio_wl64(pb, s->timestamp); /* modification time */ - avio_wl64(pb, s->timestamp); /* creation time */ + avio_wl64(pb, timestamp); /* modification time */ + avio_wl64(pb, timestamp); /* creation time */ avio_wl16(pb, 0); /* reserved */ avio_wl16(pb, 0); /* reserved */ avio_wl16(pb, gxf->audio_tracks); diff --git a/libavformat/movenc.c b/libavformat/movenc.c index ae6f603e86..78e5db7444 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -2129,6 +2129,7 @@ static int mov_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; MOVMuxContext *mov = s->priv_data; + AVDictionaryEntry *t; int i, hint_track = 0; if (!s->pb->seekable) { @@ -2259,7 +2260,18 @@ static int mov_write_header(AVFormatContext *s) } mov_write_mdat_tag(pb, mov); - mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based + +#if FF_API_TIMESTAMP + if (s->timestamp) + mov->time = s->timestamp; + else +#endif + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) { + struct tm time = {0}; + strptime(t->value, "%Y - %m - %dT%T", &time); + mov->time = mktime(&time); + } + mov->time += 0x7C25B080; //1970 based -> 1904 based if (mov->chapter_track) mov_create_chapter_track(s, mov->chapter_track); diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 16fa0dadd1..d7cc5c1c67 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -1407,6 +1407,8 @@ static int mxf_write_header(AVFormatContext *s) int i; uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0}; const int *samples_per_frame = NULL; + AVDictionaryEntry *t; + int64_t timestamp = 0; if (!s->nb_streams) return -1; @@ -1512,8 +1514,18 @@ static int mxf_write_header(AVFormatContext *s) sc->order = AV_RB32(sc->track_essence_element_key+12); } +#if FF_API_TIMESTAMP if (s->timestamp) - mxf->timestamp = mxf_parse_timestamp(s->timestamp); + timestamp = s->timestamp; + else +#endif + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) { + struct tm time = {0}; + strptime(t->value, "%Y - %m - %dT%T", &time); + timestamp = mktime(&time); + } + if (timestamp) + mxf->timestamp = mxf_parse_timestamp(timestamp); mxf->duration = -1; mxf->timecode_track = av_mallocz(sizeof(*mxf->timecode_track)); diff --git a/libavformat/version.h b/libavformat/version.h index 18331fc7d1..cae0339f62 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -83,5 +83,8 @@ #ifndef FF_API_LOOP_OUTPUT #define FF_API_LOOP_OUTPUT (LIBAVFORMAT_VERSION_MAJOR < 54) #endif +#ifndef FF_API_TIMESTAMP +#define FF_API_TIMESTAMP (LIBAVFORMAT_VERSION_MAJOR < 54) +#endif #endif /* AVFORMAT_VERSION_H */ From e59d6b4d7255d6d3dc89580f534e18af1433fe25 Mon Sep 17 00:00:00 2001 From: Oskar Arvidsson Date: Tue, 12 Jul 2011 10:52:19 +0200 Subject: [PATCH 08/13] pix_fmt: Fix number of bits per component in yuv444p9be Signed-off-by: Ronald S. Bultje --- libavutil/pixdesc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c index efc7c7ea0e..c70a41347b 100644 --- a/libavutil/pixdesc.c +++ b/libavutil/pixdesc.c @@ -918,9 +918,9 @@ const AVPixFmtDescriptor av_pix_fmt_descriptors[PIX_FMT_NB] = { .log2_chroma_w= 0, .log2_chroma_h= 0, .comp = { - {0,1,1,0,9}, /* Y */ - {1,1,1,0,9}, /* U */ - {2,1,1,0,9}, /* V */ + {0,1,1,0,8}, /* Y */ + {1,1,1,0,8}, /* U */ + {2,1,1,0,8}, /* V */ }, .flags = PIX_FMT_BE, }, From c90a2538a0bb4937cdecb852fadc3f98899624a9 Mon Sep 17 00:00:00 2001 From: "Ronald S. Bultje" Date: Tue, 12 Jul 2011 08:15:55 -0700 Subject: [PATCH 09/13] h264: move h264_mvpred.h include. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following compile error with darwin/gcc-4.2.1: In file included from libavcodec/error_resilience.c:33: libavcodec/h264.h: In function ‘decode_mb_skip’: libavcodec/h264.h:773: error: ‘always_inline’ function could not be inlined in call to ‘pred_pskip_motion’: the function body must appear before caller libavcodec/h264.h:1334: error: called from here --- libavcodec/h264.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libavcodec/h264.h b/libavcodec/h264.h index f5bdb667b9..9d46ab479a 100644 --- a/libavcodec/h264.h +++ b/libavcodec/h264.h @@ -770,7 +770,7 @@ static av_always_inline int get_chroma_qp(H264Context *h, int t, int qscale){ return h->pps.chroma_qp_table[t][qscale]; } -static av_always_inline void pred_pskip_motion(H264Context * const h); +#include "h264_mvpred.h" //For pred_pskip_motion() static void fill_decode_neighbors(H264Context *h, int mb_type){ MpegEncContext * const s = &h->s; @@ -1341,6 +1341,4 @@ static void av_unused decode_mb_skip(H264Context *h){ h->prev_mb_skipped= 1; } -#include "h264_mvpred.h" //For pred_pskip_motion() - #endif /* AVCODEC_H264_H */ From 828e20733726e8e4fcf70a19d51652948d9436b6 Mon Sep 17 00:00:00 2001 From: Diego Biurrun Date: Fri, 8 Jul 2011 15:29:57 +0200 Subject: [PATCH 10/13] dnxhdenc: Replace a forward declaration by the proper #include. --- libavcodec/dnxhdenc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libavcodec/dnxhdenc.c b/libavcodec/dnxhdenc.c index 78da1c15e2..85832a1dc3 100644 --- a/libavcodec/dnxhdenc.c +++ b/libavcodec/dnxhdenc.c @@ -28,6 +28,7 @@ #include "avcodec.h" #include "dsputil.h" #include "mpegvideo.h" +#include "mpegvideo_common.h" #include "dnxhdenc.h" #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM @@ -38,8 +39,6 @@ static const AVOption options[]={ }; static const AVClass class = { "dnxhd", av_default_item_name, options, LIBAVUTIL_VERSION_INT }; -int dct_quantize_c(MpegEncContext *s, DCTELEM *block, int n, int qscale, int *overflow); - #define LAMBDA_FRAC_BITS 10 static av_always_inline void dnxhd_get_pixels_8x4(DCTELEM *restrict block, const uint8_t *pixels, int line_size) From 987041318ff7a5e99682470a017eef06151f00ae Mon Sep 17 00:00:00 2001 From: Diego Biurrun Date: Mon, 11 Jul 2011 17:04:15 +0200 Subject: [PATCH 11/13] ffplay: skip return value of avcodec_decode_video2 / avcodec_decode_subtitle2 This fixes some warnings about unused-but-set variables. --- ffplay.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ffplay.c b/ffplay.c index 706ee25d65..cb5567d283 100644 --- a/ffplay.c +++ b/ffplay.c @@ -1457,7 +1457,7 @@ static int output_picture2(VideoState *is, AVFrame *src_frame, double pts1, int6 static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacket *pkt) { - int len1, got_picture, i; + int got_picture, i; if (packet_queue_get(&is->videoq, pkt, 1) < 0) return -1; @@ -1485,9 +1485,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacke return 0; } - len1 = avcodec_decode_video2(is->video_st->codec, - frame, &got_picture, - pkt); + avcodec_decode_video2(is->video_st->codec, frame, &got_picture, pkt); if (got_picture) { if (decoder_reorder_pts == -1) { @@ -1828,7 +1826,7 @@ static int subtitle_thread(void *arg) VideoState *is = arg; SubPicture *sp; AVPacket pkt1, *pkt = &pkt1; - int len1, got_subtitle; + int got_subtitle; double pts; int i, j; int r, g, b, y, u, v, a; @@ -1862,9 +1860,9 @@ static int subtitle_thread(void *arg) if (pkt->pts != AV_NOPTS_VALUE) pts = av_q2d(is->subtitle_st->time_base)*pkt->pts; - len1 = avcodec_decode_subtitle2(is->subtitle_st->codec, - &sp->sub, &got_subtitle, - pkt); + avcodec_decode_subtitle2(is->subtitle_st->codec, &sp->sub, + &got_subtitle, pkt); + if (got_subtitle && sp->sub.format == 0) { sp->pts = pts; From 028216b2c2d81f7fe918163c74b0a59cd327f2f7 Mon Sep 17 00:00:00 2001 From: Diego Biurrun Date: Tue, 12 Jul 2011 20:36:50 +0200 Subject: [PATCH 12/13] h264: move decode_mb_skip() from h264.h to h.264_mvpred.h This resolves a circular dependency between the headers. --- libavcodec/h264.h | 41 ---------------------------------------- libavcodec/h264_mvpred.h | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/libavcodec/h264.h b/libavcodec/h264.h index 9d46ab479a..95b471539d 100644 --- a/libavcodec/h264.h +++ b/libavcodec/h264.h @@ -770,8 +770,6 @@ static av_always_inline int get_chroma_qp(H264Context *h, int t, int qscale){ return h->pps.chroma_qp_table[t][qscale]; } -#include "h264_mvpred.h" //For pred_pskip_motion() - static void fill_decode_neighbors(H264Context *h, int mb_type){ MpegEncContext * const s = &h->s; const int mb_xy= h->mb_xy; @@ -1302,43 +1300,4 @@ static av_always_inline int get_dct8x8_allowed(H264Context *h){ return !(AV_RN64A(h->sub_mb_type) & ((MB_TYPE_16x8|MB_TYPE_8x16|MB_TYPE_8x8|MB_TYPE_DIRECT2)*0x0001000100010001ULL)); } -/** - * decodes a P_SKIP or B_SKIP macroblock - */ -static void av_unused decode_mb_skip(H264Context *h){ - MpegEncContext * const s = &h->s; - const int mb_xy= h->mb_xy; - int mb_type=0; - - memset(h->non_zero_count[mb_xy], 0, 48); - - if(MB_FIELD) - mb_type|= MB_TYPE_INTERLACED; - - if( h->slice_type_nos == AV_PICTURE_TYPE_B ) - { - // just for fill_caches. pred_direct_motion will set the real mb_type - mb_type|= MB_TYPE_L0L1|MB_TYPE_DIRECT2|MB_TYPE_SKIP; - if(h->direct_spatial_mv_pred){ - fill_decode_neighbors(h, mb_type); - fill_decode_caches(h, mb_type); //FIXME check what is needed and what not ... - } - ff_h264_pred_direct_motion(h, &mb_type); - mb_type|= MB_TYPE_SKIP; - } - else - { - mb_type|= MB_TYPE_16x16|MB_TYPE_P0L0|MB_TYPE_P1L0|MB_TYPE_SKIP; - - fill_decode_neighbors(h, mb_type); - pred_pskip_motion(h); - } - - write_back_motion(h, mb_type); - s->current_picture.f.mb_type[mb_xy] = mb_type; - s->current_picture.f.qscale_table[mb_xy] = s->qscale; - h->slice_table[ mb_xy ]= h->slice_num; - h->prev_mb_skipped= 1; -} - #endif /* AVCODEC_H264_H */ diff --git a/libavcodec/h264_mvpred.h b/libavcodec/h264_mvpred.h index 1d40c0a2c5..52c032337a 100644 --- a/libavcodec/h264_mvpred.h +++ b/libavcodec/h264_mvpred.h @@ -327,4 +327,43 @@ zeromv: return; } +/** + * decodes a P_SKIP or B_SKIP macroblock + */ +static void av_unused decode_mb_skip(H264Context *h){ + MpegEncContext * const s = &h->s; + const int mb_xy= h->mb_xy; + int mb_type=0; + + memset(h->non_zero_count[mb_xy], 0, 48); + + if(MB_FIELD) + mb_type|= MB_TYPE_INTERLACED; + + if( h->slice_type_nos == AV_PICTURE_TYPE_B ) + { + // just for fill_caches. pred_direct_motion will set the real mb_type + mb_type|= MB_TYPE_L0L1|MB_TYPE_DIRECT2|MB_TYPE_SKIP; + if(h->direct_spatial_mv_pred){ + fill_decode_neighbors(h, mb_type); + fill_decode_caches(h, mb_type); //FIXME check what is needed and what not ... + } + ff_h264_pred_direct_motion(h, &mb_type); + mb_type|= MB_TYPE_SKIP; + } + else + { + mb_type|= MB_TYPE_16x16|MB_TYPE_P0L0|MB_TYPE_P1L0|MB_TYPE_SKIP; + + fill_decode_neighbors(h, mb_type); + pred_pskip_motion(h); + } + + write_back_motion(h, mb_type); + s->current_picture.f.mb_type[mb_xy] = mb_type; + s->current_picture.f.qscale_table[mb_xy] = s->qscale; + h->slice_table[ mb_xy ]= h->slice_num; + h->prev_mb_skipped= 1; +} + #endif /* AVCODEC_H264_MVPRED_H */ From 2cb6dec61c8e6676105de628ba20a5b3d162976e Mon Sep 17 00:00:00 2001 From: Diego Biurrun Date: Fri, 8 Jul 2011 15:33:17 +0200 Subject: [PATCH 13/13] doc: Improve references to external URLs. Sometimes it makes sense to replace a raw URL with some informative text that links to the URL instead of using the raw URL itself in the text. Also transform some mailing list references into links. --- doc/developer.texi | 36 ++++++++++++++++++++++-------------- doc/faq.texi | 17 ++++++++++------- doc/ffmpeg.texi | 4 ++-- doc/general.texi | 9 ++++----- doc/protocols.texi | 2 +- 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/doc/developer.texi b/doc/developer.texi index 597ce957d1..45a4f4476b 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -31,14 +31,16 @@ only the public api is used. You can use Libav in your commercial program, but you must abide to the license, LGPL or GPL depending on the specific features used, please refer -to @url{http://libav.org/legal.html} for a quick checklist and to -@url{http://git.libav.org/?p=libav.git;a=blob;f=COPYING.GPLv2}, -@url{http://git.libav.org/?p=libav.git;a=blob;f=COPYING.GPLv3}, -@url{http://git.libav.org/?p=libav.git;a=blob;f=COPYING.LGPLv2.1}, -@url{http://git.libav.org/?p=libav.git;a=blob;f=COPYING.LGPLv3} for the -exact text of the licenses. +to @uref{http://libav.org/legal.html, our legal page} for a quick checklist and to +the following links for the exact text of each license: +@uref{http://git.libav.org/?p=libav.git;a=blob;f=COPYING.GPLv2, GPL version 2}, +@uref{http://git.libav.org/?p=libav.git;a=blob;f=COPYING.GPLv3, GPL version 3}, +@uref{http://git.libav.org/?p=libav.git;a=blob;f=COPYING.LGPLv2.1, LGPL version 2.1}, +@uref{http://git.libav.org/?p=libav.git;a=blob;f=COPYING.LGPLv3, LGPL version 3}. Any modification to the source code can be suggested for inclusion. -The best way to proceed is to send your patches to the Libav mailing list. +The best way to proceed is to send your patches to the +@uref{https://lists.libav.org/mailman/listinfo/libav-devel, libav-devel} +mailing list. @anchor{Coding Rules} @section Coding Rules @@ -177,7 +179,10 @@ should also be avoided if they don't make the code easier to understand. When applying patches that have been discussed (at length) on the mailing list, reference the thread in the log message. @item - Subscribe to the libav-devel and libav-commits mailing list. + Subscribe to the + @uref{https://lists.libav.org/mailman/listinfo/libav-devel, libav-devel} and + @uref{https://lists.libav.org/mailman/listinfo/libav-commits, libav-commits} + mailing lists. Bugs and possible improvements or general questions regarding commits are discussed on libav-devel. We expect you to react if problems with your code are uncovered. @@ -241,8 +246,9 @@ it does not cause unexpected problems. Patches should be posted as base64 encoded attachments (or any other encoding which ensures that the patch will not be trashed during -transmission) to the libav-devel mailing list, see -@url{https://lists.libav.org/mailman/listinfo/libav-devel} +transmission) to the +@uref{https://lists.libav.org/mailman/listinfo/libav-devel, libav-devel} +mailing list. It also helps quite a bit if you tell us what the patch does (for example 'replaces lrint by lrintf'), and why (for example '*BSD isn't C99 compliant @@ -314,9 +320,9 @@ send a reminder by email. Your patch should eventually be dealt with. @item Is the patch against latest Libav git master branch? @item - Are you subscribed to libav-devel? - (@url{https://lists.libav.org/mailman/listinfo/libav-devel} - the list is subscribers) + Are you subscribed to the + @uref{https://lists.libav.org/mailman/listinfo/libav-devel, libav-devel} + mailing list? (Only list subscribers are allowed to post.) @item Have you checked that the changes are minimal, so that the same cannot be achieved with a smaller patch and/or simpler final code? @@ -370,7 +376,9 @@ send a reminder by email. Your patch should eventually be dealt with. @section Patch review process -All patches posted to libav-devel will be reviewed, unless they contain a +All patches posted to the +@uref{https://lists.libav.org/mailman/listinfo/libav-devel, libav-devel} +mailing list will be reviewed, unless they contain a clear note that the patch is not for the git master branch. Reviews and comments will be posted as replies to the patch on the mailing list. The patch submitter then has to take care of every comment, diff --git a/doc/faq.texi b/doc/faq.texi index 3d7a275f89..42a135c429 100644 --- a/doc/faq.texi +++ b/doc/faq.texi @@ -24,8 +24,8 @@ help out creating them. Nowhere. We do not support old Libav versions in any way, we simply lack the time, motivation and manpower to do so. If you have a problem with an old version of Libav, upgrade to the latest git snapshot. If you -still experience the problem, then you can report it according to the -guidelines in @url{http://libav.org/bugreports.html}. +still experience the problem, then you can report it according to our +@uref{http://libav.org/bugreports.html, bug reporting guidelines}. @section Why doesn't Libav support feature [xyz]? @@ -46,7 +46,8 @@ Likely reasons @itemize @item We are busy and haven't had time yet to read your report or investigate the issue. -@item You didn't follow @url{http://libav.org/bugreports.html}. +@item You did not follow our + @uref{http://libav.org/bugreports.html, bug reporting guidelines}. @item You didn't use git master. @item You reported a segmentation fault without gdb output. @item You describe a problem but not how to reproduce it. @@ -123,8 +124,9 @@ problem and an NP-hard problem... @section ffmpeg does not work; what is wrong? -Try a @code{make distclean} in the ffmpeg source directory before the build. If this does not help see -(@url{http://libav.org/bugreports.html}). +Try a @code{make distclean} in the ffmpeg source directory before the build. +If this does not help see our +@uref{http://libav.org/bugreports.html, bug reporting guidelines}. @section How do I encode single pictures into movies? @@ -285,7 +287,8 @@ Just create an "input.avs" text file with this single line ... ffmpeg -i input.avs @end example -For ANY other help on Avisynth, please visit @url{http://www.avisynth.org/}. +For ANY other help on Avisynth, please visit the +@uref{http://www.avisynth.org/, Avisynth homepage}. @section How can I join video files? @@ -413,7 +416,7 @@ No. These tools are too bloated and they complicate the build. Libav is already organized in a highly modular manner and does not need to be rewritten in a formal object language. Further, many of the developers favor straight C; it works for them. For more arguments on this matter, -read "Programming Religion" at (@url{http://www.tux.org/lkml/#s15}). +read @uref{http://www.tux.org/lkml/#s15, "Programming Religion"}. @section I do not like the LGPL, can I contribute code under the GPL instead? diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 5246b18640..517bb0cd38 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -882,8 +882,8 @@ ffmpeg -f oss -i /dev/dsp -f video4linux2 -i /dev/video0 /tmp/out.mpg @end example Note that you must activate the right video source and channel before -launching ffmpeg with any TV viewer such as xawtv -(@url{http://linux.bytesex.org/xawtv/}) by Gerd Knorr. You also +launching ffmpeg with any TV viewer such as +@uref{http://linux.bytesex.org/xawtv/, xawtv} by Gerd Knorr. You also have to set the audio recording levels correctly with a standard mixer. diff --git a/doc/general.texi b/doc/general.texi index c97a7575c8..ace464f024 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -836,7 +836,7 @@ noticeable when running make for a second time (for example in @code{make install}). @item In order to compile FFplay, you must have the MinGW development library -of SDL. Get it from @url{http://www.libsdl.org}. +of @uref{http://www.libsdl.org/, SDL}. Edit the @file{bin/sdl-config} script so that it points to the correct prefix where SDL was installed. Verify that @file{sdl-config} can be launched from the MSYS command line. @@ -1008,8 +1008,7 @@ Then configure Libav with the following options: (you can change the cross-prefix according to the prefix chosen for the MinGW tools). -Then you can easily test Libav with Wine -(@url{http://www.winehq.com/}). +Then you can easily test Libav with @uref{http://www.winehq.com/, Wine}. @subsection Compilation under Cygwin @@ -1048,8 +1047,8 @@ If you want to build Libav with additional libraries, download Cygwin libogg-devel, libvorbis-devel @end example -These library packages are only available from Cygwin Ports -(@url{http://sourceware.org/cygwinports/}) : +These library packages are only available from +@uref{http://sourceware.org/cygwinports/, Cygwin Ports}: @example yasm, libSDL-devel, libdirac-devel, libfaac-devel, libgsm-devel, diff --git a/doc/protocols.texi b/doc/protocols.texi index 18749efbfb..59906312fa 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -242,7 +242,7 @@ data transferred over RDT). The muxer can be used to send a stream using RTSP ANNOUNCE to a server supporting it (currently Darwin Streaming Server and Mischa Spiegelmock's -RTSP server, @url{http://github.com/revmischa/rtsp-server}). +@uref{http://github.com/revmischa/rtsp-server, RTSP server}). The required syntax for a RTSP url is: @example