diff options
-rw-r--r-- | avconv.c | 77 | ||||
-rw-r--r-- | doc/avconv.texi | 17 | ||||
-rw-r--r-- | doc/filters.texi | 2 | ||||
-rw-r--r-- | doc/protocols.texi | 23 | ||||
-rw-r--r-- | ffmpeg.c | 77 | ||||
-rw-r--r-- | libavfilter/avfilter.h | 2 | ||||
-rw-r--r-- | libavfilter/vf_boxblur.c | 4 | ||||
-rw-r--r-- | libavfilter/vf_delogo.c | 8 | ||||
-rw-r--r-- | libavfilter/vf_lut.c | 12 | ||||
-rw-r--r-- | libavformat/aiffdec.c | 3 | ||||
-rw-r--r-- | libavformat/avformat.h | 11 | ||||
-rw-r--r-- | libavformat/aviobuf.c | 7 | ||||
-rw-r--r-- | libavformat/cafdec.c | 2 | ||||
-rw-r--r-- | libavformat/mov.c | 33 | ||||
-rw-r--r-- | libavformat/mpegenc.c | 33 | ||||
-rw-r--r-- | libavformat/mpegtsenc.c | 6 | ||||
-rw-r--r-- | libavformat/options.c | 2 | ||||
-rw-r--r-- | libavformat/qcp.c | 3 | ||||
-rw-r--r-- | libavformat/rtp.c | 3 | ||||
-rw-r--r-- | libavformat/rtpenc.c | 2 | ||||
-rw-r--r-- | libavformat/rtsp.c | 75 | ||||
-rw-r--r-- | libavformat/rtsp.h | 21 | ||||
-rw-r--r-- | libavformat/rtspdec.c | 8 | ||||
-rw-r--r-- | libavformat/rtspenc.c | 9 | ||||
-rw-r--r-- | libavformat/utils.c | 14 | ||||
-rw-r--r-- | libavformat/version.h | 11 | ||||
-rw-r--r-- | libavutil/opt.c | 4 |
27 files changed, 398 insertions, 71 deletions
@@ -347,6 +347,8 @@ typedef struct OptionsContext { int nb_inter_matrices; SpecifierOpt *top_field_first; int nb_top_field_first; + SpecifierOpt *presets; + int nb_presets; #if CONFIG_AVFILTER SpecifierOpt *filters; int nb_filters; @@ -3087,15 +3089,62 @@ static void parse_forced_key_frames(char *kf, OutputStream *ost, } } +static uint8_t *get_line(AVIOContext *s) +{ + AVIOContext *line; + uint8_t *buf; + char c; + + if (avio_open_dyn_buf(&line) < 0) { + av_log(NULL, AV_LOG_FATAL, "Could not alloc buffer for reading preset.\n"); + exit_program(1); + } + + while ((c = avio_r8(s)) && c != '\n') + avio_w8(line, c); + avio_w8(line, 0); + avio_close_dyn_buf(line, &buf); + + return buf; +} + +static int get_preset_file_2(const char *preset_name, const char *codec_name, AVIOContext **s) +{ + int i, ret = 1; + char filename[1000]; + const char *base[3] = { getenv("AVCONV_DATADIR"), + getenv("HOME"), + AVCONV_DATADIR, + }; + + for (i = 0; i < FF_ARRAY_ELEMS(base) && ret; i++) { + if (!base[i]) + continue; + if (codec_name) { + snprintf(filename, sizeof(filename), "%s%s/%s-%s.avpreset", base[i], + i != 1 ? "" : "/.avconv", codec_name, preset_name); + ret = avio_open(s, filename, AVIO_FLAG_READ); + } + if (ret) { + snprintf(filename, sizeof(filename), "%s%s/%s.avpreset", base[i], + i != 1 ? "" : "/.avconv", preset_name); + ret = avio_open(s, filename, AVIO_FLAG_READ); + } + } + return ret; +} + static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, enum AVMediaType type) { OutputStream *ost; AVStream *st = av_new_stream(oc, oc->nb_streams < o->nb_streamid_map ? o->streamid_map[oc->nb_streams] : 0); - int idx = oc->nb_streams - 1; + int idx = oc->nb_streams - 1, ret = 0; int64_t max_frames = INT64_MAX; char *bsf = NULL, *next, *codec_tag = NULL; AVBitStreamFilterContext *bsfc, *bsfc_prev = NULL; double qscale = -1; + char *buf = NULL, *arg = NULL, *preset = NULL; + AVIOContext *s = NULL; if (!st) { av_log(NULL, AV_LOG_FATAL, "Could not alloc stream.\n"); @@ -3117,6 +3166,31 @@ static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, e avcodec_get_context_defaults3(st->codec, ost->enc); st->codec->codec_type = type; // XXX hack, avcodec_get_context_defaults2() sets type to unknown for stream copy + MATCH_PER_STREAM_OPT(presets, str, preset, oc, st); + if (preset && (!(ret = get_preset_file_2(preset, ost->enc->name, &s)))) { + do { + buf = get_line(s); + if (!buf[0] || buf[0] == '#') { + av_free(buf); + continue; + } + if (!(arg = strchr(buf, '='))) { + av_log(NULL, AV_LOG_FATAL, "Invalid line found in the preset file.\n"); + exit_program(1); + } + *arg++ = 0; + av_dict_set(&ost->opts, buf, arg, AV_DICT_DONT_OVERWRITE); + av_free(buf); + } while (!s->eof_reached); + avio_close(s); + } + if (ret) { + av_log(NULL, AV_LOG_FATAL, + "Preset %s specified for stream %d:%d, but could not be opened.\n", + preset, ost->file_index, ost->index); + exit_program(1); + } + MATCH_PER_STREAM_OPT(max_frames, i64, max_frames, oc, st); ost->max_frames = max_frames; @@ -4027,6 +4101,7 @@ static const OptionDef options[] = { { "y", OPT_BOOL, {(void*)&file_overwrite}, "overwrite output files" }, { "c", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" }, { "codec", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" }, + { "pre", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(presets)}, "preset name", "preset" }, { "map", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" }, { "map_metadata", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_metadata}, "set metadata information of outfile from infile", "outfile[,metadata]:infile[,metadata]" }, diff --git a/doc/avconv.texi b/doc/avconv.texi index 454d382eb1..2b61dfba2d 100644 --- a/doc/avconv.texi +++ b/doc/avconv.texi @@ -186,6 +186,8 @@ codec-dependent. @var{filter_graph} is a description of the filter graph to apply to the stream. Use @code{-filters} to show all the available filters (including also sources and sinks). +@item -pre[:@var{stream_specifier}] @var{preset_name} (@emph{output,per-stream}) +Specify the preset for matching stream(s). @item -stats (@emph{global}) Print encoding progress/statistics. On by default. @@ -770,6 +772,21 @@ quality). @chapter Examples @c man begin EXAMPLES +@section Preset files + +A preset file contains a sequence of @var{option=value} pairs, one for +each line, specifying a sequence of options which can be specified also on +the command line. Lines starting with the hash ('#') character are ignored and +are used to provide comments. Empty lines are also ignored. Check the +@file{ffpresets} directory in the Libav source tree for examples. + +Preset files are specified with the @code{pre} option, this option takes a +preset name as input. Avconv searches for a file named @var{preset_name}.avpreset in +the directories @file{$AVCONV_DATADIR} (if set), and @file{$HOME/.avconv}, and in +the data directory defined at configuration time (usually @file{$PREFIX/share/avconv}) +in that order. For example, if the argument is @code{libx264-max}, it will +search for the file @file{libx264-max.avpreset}. + @section Video and Audio grabbing If you specify the input format and device then avconv can grab video diff --git a/doc/filters.texi b/doc/filters.texi index bd15b5ead4..f266f8ea9e 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -432,7 +432,7 @@ horizontal and vertical chroma subsample values. For example for the pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. @end table -The radius must be a non-negative number, and must be not greater than +The radius must be a non-negative number, and must not be greater than the value of the expression @code{min(w,h)/2} for the luma and alpha planes, and of @code{min(cw,ch)/2} for the chroma planes. diff --git a/doc/protocols.texi b/doc/protocols.texi index 991ec888af..df3248968c 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -246,12 +246,15 @@ supporting it (currently Darwin Streaming Server and Mischa Spiegelmock's The required syntax for a RTSP url is: @example -rtsp://@var{hostname}[:@var{port}]/@var{path}[?@var{options}] +rtsp://@var{hostname}[:@var{port}]/@var{path} @end example -@var{options} is a @code{&}-separated list. The following options +The following options (set on the @file{ffmpeg}/@file{ffplay} command +line, or set in code via @code{AVOption}s or in @code{avformat_open_input}), are supported: +Flags for @code{rtsp_transport}: + @table @option @item udp @@ -261,21 +264,25 @@ Use UDP as lower transport protocol. Use TCP (interleaving within the RTSP control channel) as lower transport protocol. -@item multicast +@item udp_multicast Use UDP multicast as lower transport protocol. @item http Use HTTP tunneling as lower transport protocol, which is useful for passing proxies. - -@item filter_src -Accept packets only from negotiated peer address and port. @end table Multiple lower transport protocols may be specified, in that case they are tried one at a time (if the setup of one fails, the next one is tried). For the muxer, only the @code{tcp} and @code{udp} options are supported. +Flags for @code{rtsp_flags}: + +@table @option +@item filter_src +Accept packets only from negotiated peer address and port. +@end table + When receiving data over UDP, the demuxer tries to reorder received packets (since they may arrive out of order, or packets may get lost totally). In order for this to be enabled, a maximum delay must be specified in the @@ -291,13 +298,13 @@ Example command lines: To watch a stream over UDP, with a max reordering delay of 0.5 seconds: @example -ffplay -max_delay 500000 rtsp://server/video.mp4?udp +ffplay -max_delay 500000 -rtsp_transport udp rtsp://server/video.mp4 @end example To watch a stream tunneled over HTTP: @example -ffplay rtsp://server/video.mp4?http +ffplay -rtsp_transport http rtsp://server/video.mp4 @end example To send a stream in realtime to a RTSP server, for others to watch: @@ -355,6 +355,8 @@ typedef struct OptionsContext { int nb_inter_matrices; SpecifierOpt *top_field_first; int nb_top_field_first; + SpecifierOpt *presets; + int nb_presets; #if CONFIG_AVFILTER SpecifierOpt *filters; int nb_filters; @@ -3202,15 +3204,62 @@ static void parse_forced_key_frames(char *kf, OutputStream *ost) } } +static uint8_t *get_line(AVIOContext *s) +{ + AVIOContext *line; + uint8_t *buf; + char c; + + if (avio_open_dyn_buf(&line) < 0) { + av_log(NULL, AV_LOG_FATAL, "Could not alloc buffer for reading preset.\n"); + exit_program(1); + } + + while ((c = avio_r8(s)) && c != '\n') + avio_w8(line, c); + avio_w8(line, 0); + avio_close_dyn_buf(line, &buf); + + return buf; +} + +static int get_preset_file_2(const char *preset_name, const char *codec_name, AVIOContext **s) +{ + int i, ret = 1; + char filename[1000]; + const char *base[3] = { getenv("AVCONV_DATADIR"), + getenv("HOME"), + AVCONV_DATADIR, + }; + + for (i = 0; i < FF_ARRAY_ELEMS(base) && ret; i++) { + if (!base[i]) + continue; + if (codec_name) { + snprintf(filename, sizeof(filename), "%s%s/%s-%s.avpreset", base[i], + i != 1 ? "" : "/.avconv", codec_name, preset_name); + ret = avio_open(s, filename, AVIO_FLAG_READ); + } + if (ret) { + snprintf(filename, sizeof(filename), "%s%s/%s.avpreset", base[i], + i != 1 ? "" : "/.avconv", preset_name); + ret = avio_open(s, filename, AVIO_FLAG_READ); + } + } + return ret; +} + static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, enum AVMediaType type) { OutputStream *ost; AVStream *st = av_new_stream(oc, oc->nb_streams < o->nb_streamid_map ? o->streamid_map[oc->nb_streams] : 0); - int idx = oc->nb_streams - 1; + int idx = oc->nb_streams - 1, ret = 0; int64_t max_frames = INT64_MAX; char *bsf = NULL, *next, *codec_tag = NULL; AVBitStreamFilterContext *bsfc, *bsfc_prev = NULL; double qscale = -1; + char *buf = NULL, *arg = NULL, *preset = NULL; + AVIOContext *s = NULL; if (!st) { av_log(NULL, AV_LOG_FATAL, "Could not alloc stream.\n"); @@ -3232,6 +3281,31 @@ static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, e avcodec_get_context_defaults3(st->codec, ost->enc); st->codec->codec_type = type; // XXX hack, avcodec_get_context_defaults2() sets type to unknown for stream copy + MATCH_PER_STREAM_OPT(presets, str, preset, oc, st); + if (preset && (!(ret = get_preset_file_2(preset, ost->enc->name, &s)))) { + do { + buf = get_line(s); + if (!buf[0] || buf[0] == '#') { + av_free(buf); + continue; + } + if (!(arg = strchr(buf, '='))) { + av_log(NULL, AV_LOG_FATAL, "Invalid line found in the preset file.\n"); + exit_program(1); + } + *arg++ = 0; + av_dict_set(&ost->opts, buf, arg, AV_DICT_DONT_OVERWRITE); + av_free(buf); + } while (!s->eof_reached); + avio_close(s); + } + if (ret) { + av_log(NULL, AV_LOG_FATAL, + "Preset %s specified for stream %d:%d, but could not be opened.\n", + preset, ost->file_index, ost->index); + exit_program(1); + } + MATCH_PER_STREAM_OPT(max_frames, i64, max_frames, oc, st); ost->max_frames = max_frames; @@ -4209,6 +4283,7 @@ static const OptionDef options[] = { { "y", OPT_BOOL, {(void*)&file_overwrite}, "overwrite output files" }, { "c", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" }, { "codec", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" }, + { "pre", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(presets)}, "preset name", "preset" }, { "map", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" }, { "map_meta_data", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_meta_data}, "DEPRECATED set meta data information of outfile from infile", "outfile[,metadata]:infile[,metadata]" }, diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 5c73256601..300676d41f 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -30,7 +30,7 @@ #define LIBAVFILTER_VERSION_MAJOR 2 #define LIBAVFILTER_VERSION_MINOR 43 -#define LIBAVFILTER_VERSION_MICRO 6 +#define LIBAVFILTER_VERSION_MICRO 7 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \ diff --git a/libavfilter/vf_boxblur.c b/libavfilter/vf_boxblur.c index ef5da06f40..8d82c436a1 100644 --- a/libavfilter/vf_boxblur.c +++ b/libavfilter/vf_boxblur.c @@ -81,7 +81,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) if (!args) { av_log(ctx, AV_LOG_ERROR, - "Filter expects 2 or 4 arguments, none provided\n"); + "Filter expects 2 or 4 or 6 arguments, none provided\n"); return AVERROR(EINVAL); } @@ -342,4 +342,4 @@ AVFilter avfilter_vf_boxblur = { .outputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, }, { .name = NULL}}, -}; +};
\ No newline at end of file diff --git a/libavfilter/vf_delogo.c b/libavfilter/vf_delogo.c index 66528bb3d2..bf6139ebdb 100644 --- a/libavfilter/vf_delogo.c +++ b/libavfilter/vf_delogo.c @@ -154,9 +154,9 @@ static const char *delogo_get_name(void *ctx) } static const AVClass delogo_class = { - "DelogoContext", - delogo_get_name, - delogo_options + .class_name = "DelogoContext", + .item_name = delogo_get_name, + .option = delogo_options, }; static int query_formats(AVFilterContext *ctx) @@ -285,4 +285,4 @@ AVFilter avfilter_vf_delogo = { .outputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, }, { .name = NULL}}, -}; +};
\ No newline at end of file diff --git a/libavfilter/vf_lut.c b/libavfilter/vf_lut.c index b9ade20bcc..211a294e43 100644 --- a/libavfilter/vf_lut.c +++ b/libavfilter/vf_lut.c @@ -350,21 +350,27 @@ static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { .name = NULL}}, \ } +#if CONFIG_LUT_FILTER DEFINE_LUT_FILTER(lut, "Compute and apply a lookup table to the RGB/YUV input video.", init); +#endif +#if CONFIG_LUTYUV_FILTER DEFINE_LUT_FILTER(lutyuv, "Compute and apply a lookup table to the YUV input video.", init); +#endif +#if CONFIG_LUTRGB_FILTER DEFINE_LUT_FILTER(lutrgb, "Compute and apply a lookup table to the RGB input video.", init); +#endif #if CONFIG_NEGATE_FILTER static int negate_init(AVFilterContext *ctx, const char *args, void *opaque) { LutContext *lut = ctx->priv; - char lut_params[1024]; + char lut_params[64]; if (args) sscanf(args, "%d", &lut->negate_alpha); - av_log(ctx, AV_LOG_INFO, "negate_alpha:%d\n", lut->negate_alpha); + av_log(ctx, AV_LOG_DEBUG, "negate_alpha:%d\n", lut->negate_alpha); snprintf(lut_params, sizeof(lut_params), "c0=negval:c1=negval:c2=negval:a=%s", lut->negate_alpha ? "negval" : "val"); @@ -374,4 +380,4 @@ static int negate_init(AVFilterContext *ctx, const char *args, void *opaque) DEFINE_LUT_FILTER(negate, "Negate input video.", negate_init); -#endif +#endif
\ No newline at end of file diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c index 97fecf643d..7586344430 100644 --- a/libavformat/aiffdec.c +++ b/libavformat/aiffdec.c @@ -275,9 +275,6 @@ static int aiff_read_header(AVFormatContext *s, got_sound: /* Now positioned, get the sound data start and end */ - if (st->nb_frames) - s->file_size = st->nb_frames * st->codec->block_align; - av_set_pts_info(st, 64, 1, st->codec->sample_rate); st->start_time = 0; st->duration = st->codec->frame_size ? diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 49fe08ec60..9d17869d79 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -745,10 +745,12 @@ typedef struct AVFormatContext { */ int64_t duration; +#if FF_API_FILESIZE /** * decoding: total file size, 0 if unknown */ - int64_t file_size; + attribute_deprecated int64_t file_size; +#endif /** * Decoding: total stream bitrate in bit/s, 0 if not @@ -763,7 +765,12 @@ typedef struct AVFormatContext { /* av_seek_frame() support */ int64_t data_offset; /**< offset of the first packet */ - int mux_rate; +#if FF_API_MUXRATE + /** + * use mpeg muxer private options instead + */ + attribute_deprecated int mux_rate; +#endif unsigned int packet_size; int preload; int max_delay; diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index e7c94ba1b1..9fc555ee53 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -778,13 +778,14 @@ int avio_get_str(AVIOContext *s, int maxlen, char *buf, int buflen) { int i; + if (buflen <= 0) + return AVERROR(EINVAL); // reserve 1 byte for terminating 0 buflen = FFMIN(buflen - 1, maxlen); for (i = 0; i < buflen; i++) if (!(buf[i] = avio_r8(s))) return i + 1; - if (buflen) - buf[i] = 0; + buf[i] = 0; for (; i < maxlen; i++) if (!avio_r8(s)) return i + 1; @@ -796,6 +797,8 @@ int avio_get_str(AVIOContext *s, int maxlen, char *buf, int buflen) {\ char* q = buf;\ int ret = 0;\ + if (buflen <= 0) \ + return AVERROR(EINVAL); \ while (ret + 1 < maxlen) {\ uint8_t tmp;\ uint32_t ch;\ diff --git a/libavformat/cafdec.c b/libavformat/cafdec.c index 0938e1a0d7..a1d2ff7988 100644 --- a/libavformat/cafdec.c +++ b/libavformat/cafdec.c @@ -292,8 +292,6 @@ static int read_header(AVFormatContext *s, "block size or frame size are variable.\n"); return AVERROR_INVALIDDATA; } - s->file_size = avio_size(pb); - s->file_size = FFMAX(0, s->file_size); av_set_pts_info(st, 64, 1, st->codec->sample_rate); st->start_time = 0; diff --git a/libavformat/mov.c b/libavformat/mov.c index 936ec18570..3bbfd26313 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -99,6 +99,33 @@ static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb, return 0; } +static int mov_metadata_int8(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char buf[16]; + + /* bypass padding bytes */ + avio_r8(pb); + avio_r8(pb); + avio_r8(pb); + + snprintf(buf, sizeof(buf), "%hu", avio_r8(pb)); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + +static int mov_metadata_stik(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char buf[16]; + + snprintf(buf, sizeof(buf), "%hu", avio_r8(pb)); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + static const uint32_t mac_to_unicode[128] = { 0x00C4,0x00C5,0x00C7,0x00C9,0x00D1,0x00D6,0x00DC,0x00E1, 0x00E0,0x00E2,0x00E4,0x00E3,0x00E5,0x00E7,0x00E9,0x00E8, @@ -174,6 +201,12 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) parse = mov_metadata_track_or_disc_number; break; case MKTAG( 'd','i','s','k'): key = "disc"; parse = mov_metadata_track_or_disc_number; break; + case MKTAG( 't','v','e','s'): key = "episode_sort"; + parse = mov_metadata_int8; break; + case MKTAG( 't','v','s','n'): key = "season_number"; + parse = mov_metadata_int8; break; + case MKTAG( 's','t','i','k'): key = "media_type"; + parse = mov_metadata_stik; break; } if (c->itunes_metadata && atom.size > 8) { diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c index 8ce825489d..817f541805 100644 --- a/libavformat/mpegenc.c +++ b/libavformat/mpegenc.c @@ -20,7 +20,9 @@ */ #include "libavutil/fifo.h" +#include "libavutil/log.h" #include "libavutil/mathematics.h" +#include "libavutil/opt.h" #include "libavcodec/put_bits.h" #include "avformat.h" #include "mpeg.h" @@ -56,6 +58,7 @@ typedef struct { } StreamInfo; typedef struct { + const AVClass *class; int packet_size; /* required packet size */ int packet_number; int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */ @@ -416,9 +419,12 @@ static int mpeg_mux_init(AVFormatContext *ctx) video_bitrate += codec_rate; } +#if FF_API_MUXRATE if(ctx->mux_rate){ s->mux_rate= (ctx->mux_rate + (8 * 50) - 1) / (8 * 50); - } else { + } else +#endif + if (!s->mux_rate) { /* we increase slightly the bitrate to take into account the headers. XXX: compute it exactly */ bitrate += bitrate*5/100; @@ -1227,7 +1233,23 @@ static int mpeg_mux_end(AVFormatContext *ctx) return 0; } +#define OFFSET(x) offsetof(MpegMuxContext, x) +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, {0}, 0, INT_MAX, E }, + { NULL }, +}; + +#define MPEGENC_CLASS(flavor)\ +static const AVClass flavor ## _class = {\ + .class_name = #flavor " muxer",\ + .item_name = av_default_item_name,\ + .version = LIBAVUTIL_VERSION_INT,\ + .option = options,\ +}; + #if CONFIG_MPEG1SYSTEM_MUXER +MPEGENC_CLASS(mpeg) AVOutputFormat ff_mpeg1system_muxer = { .name = "mpeg", .long_name = NULL_IF_CONFIG_SMALL("MPEG-1 System format"), @@ -1239,9 +1261,11 @@ AVOutputFormat ff_mpeg1system_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .priv_class = &mpeg_class, }; #endif #if CONFIG_MPEG1VCD_MUXER +MPEGENC_CLASS(vcd) AVOutputFormat ff_mpeg1vcd_muxer = { .name = "vcd", .long_name = NULL_IF_CONFIG_SMALL("MPEG-1 System format (VCD)"), @@ -1252,9 +1276,11 @@ AVOutputFormat ff_mpeg1vcd_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .priv_class = &vcd_class, }; #endif #if CONFIG_MPEG2VOB_MUXER +MPEGENC_CLASS(vob) AVOutputFormat ff_mpeg2vob_muxer = { .name = "vob", .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS format (VOB)"), @@ -1266,11 +1292,13 @@ AVOutputFormat ff_mpeg2vob_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .priv_class = &vob_class, }; #endif /* Same as mpeg2vob_mux except that the pack size is 2324 */ #if CONFIG_MPEG2SVCD_MUXER +MPEGENC_CLASS(svcd) AVOutputFormat ff_mpeg2svcd_muxer = { .name = "svcd", .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS format (VOB)"), @@ -1282,11 +1310,13 @@ AVOutputFormat ff_mpeg2svcd_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .priv_class = &svcd_class, }; #endif /* Same as mpeg2vob_mux except the 'is_dvd' flag is set to produce NAV pkts */ #if CONFIG_MPEG2DVD_MUXER +MPEGENC_CLASS(dvd) AVOutputFormat ff_mpeg2dvd_muxer = { .name = "dvd", .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS format (DVD VOB)"), @@ -1298,5 +1328,6 @@ AVOutputFormat ff_mpeg2dvd_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .priv_class = &dvd_class, }; #endif diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 1173f69d78..1b00251b1c 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -92,6 +92,7 @@ static const AVOption options[] = { {"mpegts_m2ts_mode", "Enable m2ts mode.", offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_INT, {.dbl = -1 }, -1,1, AV_OPT_FLAG_ENCODING_PARAM}, + { "muxrate", NULL, offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, {1}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { NULL }, }; @@ -562,7 +563,10 @@ static int mpegts_write_header(AVFormatContext *s) service->pcr_pid = ts_st->pid; } - ts->mux_rate = s->mux_rate ? s->mux_rate : 1; +#if FF_API_MUXRATE + if (s->mux_rate) + ts->mux_rate = s->mux_rate; +#endif if (ts->mux_rate > 1) { service->pcr_packet_period = (ts->mux_rate * PCR_RETRANS_TIME) / diff --git a/libavformat/options.c b/libavformat/options.c index c099ea63a1..64bc7a8996 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -74,7 +74,9 @@ static const AVClass *format_child_class_next(const AVClass *prev) static const AVOption options[]={ {"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT, {.dbl = 5000000 }, 32, INT_MAX, D}, +#if FF_API_MUXRATE {"muxrate", "set mux rate", OFFSET(mux_rate), AV_OPT_TYPE_INT, {.dbl = DEFAULT }, 0, INT_MAX, E}, +#endif {"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.dbl = DEFAULT }, 0, INT_MAX, E}, {"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.dbl = DEFAULT }, INT_MIN, INT_MAX, D|E, "fflags"}, {"ignidx", "ignore index", 0, AV_OPT_TYPE_CONST, {.dbl = AVFMT_FLAG_IGNIDX }, INT_MIN, INT_MAX, D, "fflags"}, diff --git a/libavformat/qcp.c b/libavformat/qcp.c index 0f761a8ec1..4cff63c0ca 100644 --- a/libavformat/qcp.c +++ b/libavformat/qcp.c @@ -92,8 +92,7 @@ static int qcp_read_header(AVFormatContext *s, AVFormatParameters *ap) return AVERROR(ENOMEM); avio_rb32(pb); // "RIFF" - s->file_size = avio_rl32(pb) + 8; - avio_skip(pb, 8 + 4 + 1 + 1); // "QLCMfmt " + chunk-size + major-version + minor-version + avio_skip(pb, 4 + 8 + 4 + 1 + 1); // filesize + "QLCMfmt " + chunk-size + major-version + minor-version st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->channels = 1; diff --git a/libavformat/rtp.c b/libavformat/rtp.c index d85b1b79db..98d96160db 100644 --- a/libavformat/rtp.c +++ b/libavformat/rtp.c @@ -98,7 +98,8 @@ int ff_rtp_get_payload_type(AVFormatContext *fmt, AVCodecContext *codec) /* Was the payload type already specified for the RTP muxer? */ if (ofmt && ofmt->priv_class) { int64_t payload_type; - if (av_opt_get_int(fmt->priv_data, "payload_type", 0, &payload_type) >= 0) + if (av_opt_get_int(fmt->priv_data, "payload_type", 0, &payload_type) >= 0 && + payload_type >= 0) return (int)payload_type; } diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index bf183e7d21..83f728bc37 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -465,7 +465,7 @@ AVOutputFormat ff_rtp_muxer = { .long_name = NULL_IF_CONFIG_SMALL("RTP output format"), .priv_data_size = sizeof(RTPMuxContext), .audio_codec = CODEC_ID_PCM_MULAW, - .video_codec = CODEC_ID_NONE, + .video_codec = CODEC_ID_MPEG4, .write_header = rtp_write_header, .write_packet = rtp_write_packet, .write_trailer = rtp_write_trailer, diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 64e29cc487..cc47f92e69 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -45,6 +45,7 @@ #include "rtpdec_formats.h" #include "rtpenc_chain.h" #include "url.h" +#include "rtpenc.h" //#define DEBUG @@ -56,6 +57,36 @@ #define SDP_MAX_SIZE 16384 #define RECVBUF_SIZE 10 * RTP_MAX_PACKET_LENGTH +#define OFFSET(x) offsetof(RTSPState, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +#define ENC AV_OPT_FLAG_ENCODING_PARAM + +#define RTSP_FLAG_OPTS(name, longname) \ + { name, longname, OFFSET(rtsp_flags), AV_OPT_TYPE_FLAGS, {0}, INT_MIN, INT_MAX, DEC, "rtsp_flags" }, \ + { "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" } + +const AVOption ff_rtsp_options[] = { + { "initial_pause", "Don't start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_INT, {0}, 0, 1, DEC }, + FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags), + { "rtsp_transport", "RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" }, \ + { "udp", "UDP", 0, AV_OPT_TYPE_CONST, {1 << RTSP_LOWER_TRANSPORT_UDP}, 0, 0, DEC|ENC, "rtsp_transport" }, \ + { "tcp", "TCP", 0, AV_OPT_TYPE_CONST, {1 << RTSP_LOWER_TRANSPORT_TCP}, 0, 0, DEC|ENC, "rtsp_transport" }, \ + { "udp_multicast", "UDP multicast", 0, AV_OPT_TYPE_CONST, {1 << RTSP_LOWER_TRANSPORT_UDP_MULTICAST}, 0, 0, DEC, "rtsp_transport" }, + { "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {(1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" }, + RTSP_FLAG_OPTS("rtsp_flags", "RTSP flags"), + { NULL }, +}; + +static const AVOption sdp_options[] = { + RTSP_FLAG_OPTS("sdp_flags", "SDP flags"), + { NULL }, +}; + +static const AVOption rtp_options[] = { + RTSP_FLAG_OPTS("rtp_flags", "RTP flags"), + { NULL }, +}; + static void get_word_until_chars(char *buf, int buf_size, const char *sep, const char **pp) { @@ -1218,7 +1249,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, case RTSP_LOWER_TRANSPORT_UDP: { char url[1024], options[30] = ""; - if (rt->filter_source) + if (rt->rtsp_flags & RTSP_FLAG_FILTER_SRC) av_strlcpy(options, "?connect=1", sizeof(options)); /* Use source address if specified */ if (reply->transports[0].source[0]) { @@ -1308,8 +1339,17 @@ int ff_rtsp_connect(AVFormatContext *s) if (!ff_network_init()) return AVERROR(EIO); -redirect: + rt->control_transport = RTSP_MODE_PLAIN; + if (rt->lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_HTTP)) { + rt->lower_transport_mask = 1 << RTSP_LOWER_TRANSPORT_TCP; + rt->control_transport = RTSP_MODE_TUNNEL; + } + /* Only pass through valid flags from here */ + rt->lower_transport_mask &= (1 << RTSP_LOWER_TRANSPORT_NB) - 1; + +redirect: + lower_transport_mask = rt->lower_transport_mask; /* extract hostname and port */ av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port, path, sizeof(path), s->filename); @@ -1319,6 +1359,7 @@ redirect: if (port < 0) port = RTSP_DEFAULT_PORT; +#if FF_API_RTSP_URL_OPTIONS /* search for options */ option_list = strrchr(path, '?'); if (option_list) { @@ -1326,6 +1367,7 @@ redirect: * the options back into the same string. */ filename = option_list; while (option_list) { + int handled = 1; /* move the option pointer */ option = ++option_list; option_list = strchr(option_list, '&'); @@ -1343,7 +1385,7 @@ redirect: lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP); rt->control_transport = RTSP_MODE_TUNNEL; } else if (!strcmp(option, "filter_src")) { - rt->filter_source = 1; + rt->rtsp_flags |= RTSP_FLAG_FILTER_SRC; } else { /* Write options back into the buffer, using memmove instead * of strcpy since the strings may overlap. */ @@ -1351,10 +1393,16 @@ redirect: memmove(++filename, option, len); filename += len; if (option_list) *filename = '&'; + handled = 0; } + if (handled) + av_log(s, AV_LOG_WARNING, "Options passed via URL are " + "deprecated, use -rtsp_transport " + "and -rtsp_flags instead.\n"); } *filename = 0; } +#endif if (!lower_transport_mask) lower_transport_mask = (1 << RTSP_LOWER_TRANSPORT_NB) - 1; @@ -1797,8 +1845,9 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); ff_url_join(url, sizeof(url), "rtp", NULL, namebuf, rtsp_st->sdp_port, - "?localport=%d&ttl=%d", rtsp_st->sdp_port, - rtsp_st->sdp_ttl); + "?localport=%d&ttl=%d&connect=%d", rtsp_st->sdp_port, + rtsp_st->sdp_ttl, + rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0); if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE) < 0) { err = AVERROR_INVALIDDATA; goto fail; @@ -1820,6 +1869,13 @@ static int sdp_read_close(AVFormatContext *s) return 0; } +static const AVClass sdp_demuxer_class = { + .class_name = "SDP demuxer", + .item_name = av_default_item_name, + .option = sdp_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVInputFormat ff_sdp_demuxer = { .name = "sdp", .long_name = NULL_IF_CONFIG_SMALL("SDP"), @@ -1828,6 +1884,7 @@ AVInputFormat ff_sdp_demuxer = { .read_header = sdp_read_header, .read_packet = ff_rtsp_fetch_packet, .read_close = sdp_read_close, + .priv_class = &sdp_demuxer_class }; #endif /* CONFIG_SDP_DEMUXER */ @@ -1924,6 +1981,13 @@ fail: return ret; } +static const AVClass rtp_demuxer_class = { + .class_name = "RTP demuxer", + .item_name = av_default_item_name, + .option = rtp_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVInputFormat ff_rtp_demuxer = { .name = "rtp", .long_name = NULL_IF_CONFIG_SMALL("RTP input format"), @@ -1933,6 +1997,7 @@ AVInputFormat ff_rtp_demuxer = { .read_packet = ff_rtsp_fetch_packet, .read_close = sdp_read_close, .flags = AVFMT_NOFILE, + .priv_class = &rtp_demuxer_class }; #endif /* CONFIG_RTP_DEMUXER */ diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 7d0dac12e2..da61829da2 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -29,6 +29,7 @@ #include "httpauth.h" #include "libavutil/log.h" +#include "libavutil/opt.h" /** * Network layer over which RTP/etc packet data will be transported. @@ -37,7 +38,10 @@ enum RTSPLowerTransport { RTSP_LOWER_TRANSPORT_UDP = 0, /**< UDP/unicast */ RTSP_LOWER_TRANSPORT_TCP = 1, /**< TCP; interleaved in RTSP */ RTSP_LOWER_TRANSPORT_UDP_MULTICAST = 2, /**< UDP/multicast */ - RTSP_LOWER_TRANSPORT_NB + RTSP_LOWER_TRANSPORT_NB, + RTSP_LOWER_TRANSPORT_HTTP = 8, /**< HTTP tunneled - not a proper + transport mode as such, + only for use via AVOptions */ }; /** @@ -313,10 +317,6 @@ typedef struct RTSPState { /** Reusable buffer for receiving packets */ uint8_t* recvbuf; - /** Filter incoming UDP packets - receive packets only from the right - * source address and port. */ - int filter_source; - /** * A mask with all requested transport methods */ @@ -349,8 +349,17 @@ typedef struct RTSPState { /** Whether the server accepts the x-Dynamic-Rate header */ int accept_dynamic_rate; + + /** + * Various option flags for the RTSP muxer/demuxer. + */ + int rtsp_flags; } RTSPState; +#define RTSP_FLAG_FILTER_SRC 0x1 /**< Filter incoming UDP packets - + receive packets only from the right + source address and port. */ + /** * Describes a single stream, as identified by a single m= line block in the * SDP content. In the case of RDT, one RTSPStream can represent multiple @@ -537,4 +546,6 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, */ void ff_rtsp_undo_setup(AVFormatContext *s); +extern const AVOption ff_rtsp_options[]; + #endif /* AVFORMAT_RTSP_H */ diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index 3235365842..f55b8cf2a2 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -22,7 +22,6 @@ #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" -#include "libavutil/opt.h" #include "avformat.h" #include "internal.h" @@ -388,15 +387,10 @@ static int rtsp_read_close(AVFormatContext *s) return 0; } -static const AVOption options[] = { - { "initial_pause", "Don't start playing the stream immediately", offsetof(RTSPState, initial_pause), AV_OPT_TYPE_INT, {.dbl = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, - { NULL }, -}; - const AVClass rtsp_demuxer_class = { .class_name = "RTSP demuxer", .item_name = av_default_item_name, - .option = options, + .option = ff_rtsp_options, .version = LIBAVUTIL_VERSION_INT, }; diff --git a/libavformat/rtspenc.c b/libavformat/rtspenc.c index 5196bf4fcd..b54ae28eb8 100644 --- a/libavformat/rtspenc.c +++ b/libavformat/rtspenc.c @@ -33,20 +33,13 @@ #include "libavutil/intreadwrite.h" #include "libavutil/avstring.h" #include "url.h" -#include "libavutil/opt.h" -#include "rtpenc.h" #define SDP_MAX_SIZE 16384 -static const AVOption options[] = { - FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags), - { NULL }, -}; - static const AVClass rtsp_muxer_class = { .class_name = "RTSP muxer", .item_name = av_default_item_name, - .option = options, + .option = ff_rtsp_options, .version = LIBAVUTIL_VERSION_INT, }; diff --git a/libavformat/utils.c b/libavformat/utils.c index c7eb93f3b0..333baa0290 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -1902,7 +1902,7 @@ static int has_duration(AVFormatContext *ic) static void update_stream_timings(AVFormatContext *ic) { int64_t start_time, start_time1, start_time_text, end_time, end_time1; - int64_t duration, duration1; + int64_t duration, duration1, filesize; int i; AVStream *st; @@ -1945,9 +1945,9 @@ static void update_stream_timings(AVFormatContext *ic) if (duration != INT64_MIN && ic->duration == AV_NOPTS_VALUE) { ic->duration = duration; } - if (ic->file_size > 0 && ic->duration != AV_NOPTS_VALUE) { + if (ic->pb && (filesize = avio_size(ic->pb)) > 0 && ic->duration != AV_NOPTS_VALUE) { /* compute the bitrate */ - ic->bit_rate = (double)ic->file_size * 8.0 * AV_TIME_BASE / + ic->bit_rate = (double)filesize * 8.0 * AV_TIME_BASE / (double)ic->duration; } } @@ -1988,9 +1988,8 @@ static void estimate_timings_from_bit_rate(AVFormatContext *ic) /* if duration is already set, we believe it */ if (ic->duration == AV_NOPTS_VALUE && - ic->bit_rate != 0 && - ic->file_size != 0) { - filesize = ic->file_size; + ic->bit_rate != 0) { + filesize = ic->pb ? avio_size(ic->pb) : 0; if (filesize > 0) { for(i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; @@ -2034,7 +2033,7 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) /* estimate the end time (duration) */ /* XXX: may need to support wrapping */ - filesize = ic->file_size; + filesize = ic->pb ? avio_size(ic->pb) : 0; end_time = AV_NOPTS_VALUE; do{ offset = filesize - (DURATION_MAX_READ_SIZE<<retry); @@ -2098,7 +2097,6 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) if (file_size < 0) file_size = 0; } - ic->file_size = file_size; if ((!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts")) && diff --git a/libavformat/version.h b/libavformat/version.h index fc27815f41..1f36544b70 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -25,7 +25,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 53 #define LIBAVFORMAT_VERSION_MINOR 16 -#define LIBAVFORMAT_VERSION_MICRO 0 +#define LIBAVFORMAT_VERSION_MICRO 1 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ @@ -89,5 +89,14 @@ #ifndef FF_API_TIMESTAMP #define FF_API_TIMESTAMP (LIBAVFORMAT_VERSION_MAJOR < 54) #endif +#ifndef FF_API_FILESIZE +#define FF_API_FILESIZE (LIBAVFORMAT_VERSION_MAJOR < 54) +#endif +#ifndef FF_API_MUXRATE +#define FF_API_MUXRATE (LIBAVFORMAT_VERSION_MAJOR < 54) +#endif +#ifndef FF_API_RTSP_URL_OPTIONS +#define FF_API_RTSP_URL_OPTIONS (LIBAVFORMAT_VERSION_MAJOR < 54) +#endif #endif /* AVFORMAT_VERSION_H */ diff --git a/libavutil/opt.c b/libavutil/opt.c index db4722b068..0666a60d66 100644 --- a/libavutil/opt.c +++ b/libavutil/opt.c @@ -503,7 +503,8 @@ int av_opt_get_q(void *obj, const char *name, int search_flags, AVRational *out_ int av_opt_flag_is_set(void *obj, const char *field_name, const char *flag_name) { const AVOption *field = av_opt_find(obj, field_name, NULL, 0, 0); - const AVOption *flag = av_opt_find(obj, flag_name, NULL, 0, 0); + const AVOption *flag = av_opt_find(obj, flag_name, + field ? field->unit : NULL, 0, 0); int64_t res; if (!field || !flag || flag->type != AV_OPT_TYPE_CONST || @@ -700,6 +701,7 @@ int av_set_options_string(void *ctx, const char *opts, if (!opts) return 0; + while (*opts) { if ((ret = parse_key_value_pair(ctx, &opts, key_val_sep, pairs_sep)) < 0) return ret; |