From f05563531338e4c35a3e17bcb82769f194bb7ee3 Mon Sep 17 00:00:00 2001 From: Raivo Hool Date: Fri, 14 Oct 2011 20:14:18 +0300 Subject: [PATCH 01/20] mov: add support for TV metadata atoms tves, tvsn and stik Signed-off-by: Anton Khirnov --- libavformat/mov.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/libavformat/mov.c b/libavformat/mov.c index 0463c17cba..144fdb9a15 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, @@ -172,6 +199,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) { From c10731e78b3ed89090b808b0919947f5729841c3 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sun, 9 Oct 2011 14:12:14 +0200 Subject: [PATCH 02/20] lavf: deprecate AVFormatContext.file_size It's too unreliable to be useful. avio_size() should be called instead. --- libavformat/aiffdec.c | 3 --- libavformat/avformat.h | 4 +++- libavformat/cafdec.c | 2 -- libavformat/qcp.c | 3 +-- libavformat/utils.c | 14 ++++++-------- libavformat/version.h | 3 +++ 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c index 34e6d88ab9..54972de5e0 100644 --- a/libavformat/aiffdec.c +++ b/libavformat/aiffdec.c @@ -268,9 +268,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 70d466e0d1..61b231ec1d 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -716,10 +716,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 diff --git a/libavformat/cafdec.c b/libavformat/cafdec.c index 38cde3be50..02d1e3ccaa 100644 --- a/libavformat/cafdec.c +++ b/libavformat/cafdec.c @@ -286,8 +286,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/qcp.c b/libavformat/qcp.c index aefb076e3b..291eb182ed 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/utils.c b/libavformat/utils.c index 3115723668..6393b62615 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -1837,7 +1837,7 @@ static int has_duration(AVFormatContext *ic) static void update_stream_timings(AVFormatContext *ic) { int64_t start_time, start_time1, end_time, end_time1; - int64_t duration, duration1; + int64_t duration, duration1, filesize; int i; AVStream *st; @@ -1872,9 +1872,9 @@ static void update_stream_timings(AVFormatContext *ic) } if (duration != INT64_MIN) { ic->duration = duration; - if (ic->file_size > 0) { + if (ic->pb && (filesize = avio_size(ic->pb)) > 0) { /* 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; } } @@ -1916,9 +1916,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]; @@ -1962,7 +1961,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<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 53c585a06b..1a562d6a87 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -86,5 +86,8 @@ #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 #endif /* AVFORMAT_VERSION_H */ From 5a9ee3152b6e69166c7819f07a4992bd22052917 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sun, 9 Oct 2011 14:40:11 +0200 Subject: [PATCH 03/20] mpegenc/mpegtsenc: add muxrate private options. Deprecate AVFormatContext.mux_rate. --- libavformat/avformat.h | 7 ++++++- libavformat/mpegenc.c | 33 ++++++++++++++++++++++++++++++++- libavformat/mpegtsenc.c | 6 +++++- libavformat/options.c | 2 ++ libavformat/version.h | 3 +++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 61b231ec1d..4e7e883eb5 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -736,7 +736,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/mpegenc.c b/libavformat/mpegenc.c index 58242060fa..5e8954c902 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 7347caab5e..d600d18b62 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -88,6 +88,7 @@ static const AVOption options[] = { offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, {.dbl = 0x1000 }, 0x1000, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM}, { "mpegts_start_pid", "Set the first pid.", offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, {.dbl = 0x0100 }, 0x0100, 0x0f00, 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 }, }; @@ -539,7 +540,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 ddace58a4e..23a7e65369 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/version.h b/libavformat/version.h index 1a562d6a87..2bebed9799 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -89,5 +89,8 @@ #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 #endif /* AVFORMAT_VERSION_H */ From ab2940691ba76e1a9b0ce608db0dfc45021d741e Mon Sep 17 00:00:00 2001 From: Alex Converse Date: Fri, 14 Oct 2011 18:27:59 -0700 Subject: [PATCH 04/20] avio: Check for invalid buffer length. --- libavformat/aviobuf.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index b5e9d4c61c..8f3599a9dc 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -769,13 +769,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; @@ -787,6 +788,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;\ From 85afbb1d00d58812df5d634e946b2fcf653bcd8e Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sat, 1 Oct 2011 14:42:53 +0200 Subject: [PATCH 05/20] AVOptions: don't segfault on NULL parameter in av_set_options_string() --- libavutil/opt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libavutil/opt.c b/libavutil/opt.c index 7c80135f5e..bf63bef2af 100644 --- a/libavutil/opt.c +++ b/libavutil/opt.c @@ -696,6 +696,9 @@ int av_set_options_string(void *ctx, const char *opts, { int ret, count = 0; + if (!opts) + return 0; + while (*opts) { if ((ret = parse_key_value_pair(ctx, &opts, key_val_sep, pairs_sep)) < 0) return ret; From 8fe0c527f33fe6d2e89d62a23ae912553572452b Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Sat, 28 May 2011 21:51:03 +0200 Subject: [PATCH 06/20] lavfi: add LUT (LookUp Table) generic filters Signed-off-by: Anton Khirnov --- Changelog | 1 + doc/filters.texi | 112 ++++++++++++ libavfilter/Makefile | 3 + libavfilter/allfilters.c | 3 + libavfilter/avfilter.h | 4 +- libavfilter/formats.c | 12 ++ libavfilter/internal.h | 3 + libavfilter/vf_lut.c | 371 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 libavfilter/vf_lut.c diff --git a/Changelog b/Changelog index c2f16b367b..8374e05342 100644 --- a/Changelog +++ b/Changelog @@ -50,6 +50,7 @@ easier to use. The changes are: - Apple ProRes decoder - CELT in Ogg demuxing - VC-1 interlaced decoding +- lut, lutrgb, and lutyuv filters version 0.7: diff --git a/doc/filters.texi b/doc/filters.texi index 46274acd54..f7aacf2e19 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -701,6 +701,118 @@ a float number which specifies chroma temporal strength, defaults to @var{luma_tmp}*@var{chroma_spatial}/@var{luma_spatial} @end table +@section lut, lutrgb, lutyuv + +Compute a look-up table for binding each pixel component input value +to an output value, and apply it to input video. + +@var{lutyuv} applies a lookup table to a YUV input video, @var{lutrgb} +to an RGB input video. + +These filters accept in input a ":"-separated list of options, which +specify the expressions used for computing the lookup table for the +corresponding pixel component values. + +The @var{lut} filter requires either YUV or RGB pixel formats in +input, and accepts the options: +@table @option +@var{c0} (first pixel component) +@var{c1} (second pixel component) +@var{c2} (third pixel component) +@var{c3} (fourth pixel component, corresponds to the alpha component) +@end table + +The exact component associated to each option depends on the format in +input. + +The @var{lutrgb} filter requires RGB pixel formats in input, and +accepts the options: +@table @option +@var{r} (red component) +@var{g} (green component) +@var{b} (blue component) +@var{a} (alpha component) +@end table + +The @var{lutyuv} filter requires YUV pixel formats in input, and +accepts the options: +@table @option +@var{y} (Y/luminance component) +@var{u} (U/Cb component) +@var{v} (V/Cr component) +@var{a} (alpha component) +@end table + +The expressions can contain the following constants and functions: + +@table @option +@item E, PI, PHI +the corresponding mathematical approximated values for e +(euler number), pi (greek PI), PHI (golden ratio) + +@item w, h +the input width and heigth + +@item val +input value for the pixel component + +@item clipval +the input value clipped in the @var{minval}-@var{maxval} range + +@item maxval +maximum value for the pixel component + +@item minval +minimum value for the pixel component + +@item negval +the negated value for the pixel component value clipped in the +@var{minval}-@var{maxval} range , it corresponds to the expression +"maxval-clipval+minval" + +@item clip(val) +the computed value in @var{val} clipped in the +@var{minval}-@var{maxval} range + +@item gammaval(gamma) +the computed gamma correction value of the pixel component value +clipped in the @var{minval}-@var{maxval} range, corresponds to the +expression +"pow((clipval-minval)/(maxval-minval)\,@var{gamma})*(maxval-minval)+minval" + +@end table + +All expressions default to "val". + +Some examples follow: +@example +# negate input video +lutrgb="r=maxval+minval-val:g=maxval+minval-val:b=maxval+minval-val" +lutyuv="y=maxval+minval-val:u=maxval+minval-val:v=maxval+minval-val" + +# the above is the same as +lutrgb="r=negval:g=negval:b=negval" +lutyuv="y=negval:u=negval:v=negval" + +# negate luminance +lutyuv=negval + +# remove chroma components, turns the video into a graytone image +lutyuv="u=128:v=128" + +# apply a luma burning effect +lutyuv="y=2*val" + +# remove green and blue components +lutrgb="g=0:b=0" + +# set a constant alpha channel value on input +format=rgba,lutrgb=a="maxval-minval/2" + +# correct luminance gamma by a 0.5 factor +lutyuv=y=gammaval(0.5) +@end example + @section noformat Force libavfilter not to use any of the specified pixel formats for the diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 4bd06e9f2f..9659fd692f 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -33,6 +33,9 @@ OBJS-$(CONFIG_FREI0R_FILTER) += vf_frei0r.o OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o OBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o OBJS-$(CONFIG_HQDN3D_FILTER) += vf_hqdn3d.o +OBJS-$(CONFIG_LUT_FILTER) += vf_lut.o +OBJS-$(CONFIG_LUTRGB_FILTER) += vf_lut.o +OBJS-$(CONFIG_LUTYUV_FILTER) += vf_lut.o OBJS-$(CONFIG_NOFORMAT_FILTER) += vf_format.o OBJS-$(CONFIG_NULL_FILTER) += vf_null.o OBJS-$(CONFIG_OCV_FILTER) += vf_libopencv.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index dbbe441859..1357322302 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -54,6 +54,9 @@ void avfilter_register_all(void) REGISTER_FILTER (GRADFUN, gradfun, vf); REGISTER_FILTER (HFLIP, hflip, vf); REGISTER_FILTER (HQDN3D, hqdn3d, vf); + REGISTER_FILTER (LUT, lut, vf); + REGISTER_FILTER (LUTRGB, lutrgb, vf); + REGISTER_FILTER (LUTYUV, lutyuv, vf); REGISTER_FILTER (NOFORMAT, noformat, vf); REGISTER_FILTER (NULL, null, vf); REGISTER_FILTER (OCV, ocv, vf); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index c46dd1f5dd..d4c03792da 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -29,8 +29,8 @@ #include "libavutil/rational.h" #define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 7 -#define LIBAVFILTER_VERSION_MICRO 2 +#define LIBAVFILTER_VERSION_MINOR 8 +#define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \ diff --git a/libavfilter/formats.c b/libavfilter/formats.c index ae916cf16c..848b2ee119 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -21,6 +21,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "internal.h" /** * Add all refs from a to ret and destroy a. @@ -70,6 +71,17 @@ AVFilterFormats *avfilter_merge_formats(AVFilterFormats *a, AVFilterFormats *b) return ret; } +int ff_fmt_is_in(int fmt, const int *fmts) +{ + const int *p; + + for (p = fmts; *p != PIX_FMT_NONE; p++) { + if (fmt == *p) + return 1; + } + return 0; +} + AVFilterFormats *avfilter_make_format_list(const int *fmts) { AVFilterFormats *formats; diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 64b3f3b865..0630e9b7d6 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -52,4 +52,7 @@ int ff_avfilter_graph_config_formats(AVFilterGraph *graphctx, AVClass *log_ctx); /** default handler for freeing audio/video buffer when there are no references left */ void ff_avfilter_default_free_buffer(AVFilterBuffer *buf); +/** Tell is a format is contained in the provided list terminated by -1. */ +int ff_fmt_is_in(int fmt, const int *fmts); + #endif /* AVFILTER_INTERNAL_H */ diff --git a/libavfilter/vf_lut.c b/libavfilter/vf_lut.c new file mode 100644 index 0000000000..d7fab5e17d --- /dev/null +++ b/libavfilter/vf_lut.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2011 Stefano Sabatini + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Compute a look-up table for binding the input value to the output + * value, and apply it to input video. + */ + +#include "libavutil/eval.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "internal.h" + +static const char *var_names[] = { + "E", + "PHI", + "PI", + "w", ///< width of the input video + "h", ///< height of the input video + "val", ///< input value for the pixel + "maxval", ///< max value for the pixel + "minval", ///< min value for the pixel + "negval", ///< negated value + "clipval", + NULL +}; + +enum var_name { + VAR_E, + VAR_PHI, + VAR_PI, + VAR_W, + VAR_H, + VAR_VAL, + VAR_MAXVAL, + VAR_MINVAL, + VAR_NEGVAL, + VAR_CLIPVAL, + VAR_VARS_NB +}; + +typedef struct { + const AVClass *class; + uint8_t lut[4][256]; ///< lookup table for each component + char *comp_expr_str[4]; + AVExpr *comp_expr[4]; + int hsub, vsub; + double var_values[VAR_VARS_NB]; + int is_rgb, is_yuv; + int rgba_map[4]; + int step; +} LutContext; + +#define Y 0 +#define U 1 +#define V 2 +#define R 0 +#define G 1 +#define B 2 +#define A 3 + +#define OFFSET(x) offsetof(LutContext, x) + +static const AVOption lut_options[] = { + {"c0", "set component #0 expression", OFFSET(comp_expr_str[0]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"c1", "set component #1 expression", OFFSET(comp_expr_str[1]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"c2", "set component #2 expression", OFFSET(comp_expr_str[2]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"c3", "set component #3 expression", OFFSET(comp_expr_str[3]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"y", "set Y expression", OFFSET(comp_expr_str[Y]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"u", "set U expression", OFFSET(comp_expr_str[U]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"v", "set V expression", OFFSET(comp_expr_str[V]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"r", "set R expression", OFFSET(comp_expr_str[R]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"g", "set G expression", OFFSET(comp_expr_str[G]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"b", "set B expression", OFFSET(comp_expr_str[B]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {"a", "set A expression", OFFSET(comp_expr_str[A]), FF_OPT_TYPE_STRING, {.str="val"}, CHAR_MIN, CHAR_MAX}, + {NULL}, +}; + +static const char *lut_get_name(void *ctx) +{ + return "lut"; +} + +static const AVClass lut_class = { + "LutContext", + lut_get_name, + lut_options +}; + +static int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + LutContext *lut = ctx->priv; + int ret; + + lut->class = &lut_class; + av_opt_set_defaults(lut); + + lut->var_values[VAR_PHI] = M_PHI; + lut->var_values[VAR_PI] = M_PI; + lut->var_values[VAR_E ] = M_E; + + lut->is_rgb = !strcmp(ctx->filter->name, "lutrgb"); + lut->is_yuv = !strcmp(ctx->filter->name, "lutyuv"); + if (args && (ret = av_set_options_string(lut, args, "=", ":")) < 0) + return ret; + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + LutContext *lut = ctx->priv; + int i; + + for (i = 0; i < 4; i++) { + av_expr_free(lut->comp_expr[i]); + lut->comp_expr[i] = NULL; + av_freep(&lut->comp_expr_str[i]); + } +} + +#define YUV_FORMATS \ + PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV420P, \ + PIX_FMT_YUV411P, PIX_FMT_YUV410P, PIX_FMT_YUV440P, \ + PIX_FMT_YUVA420P, \ + PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P, \ + PIX_FMT_YUVJ440P + +#define RGB_FORMATS \ + PIX_FMT_ARGB, PIX_FMT_RGBA, \ + PIX_FMT_ABGR, PIX_FMT_BGRA, \ + PIX_FMT_RGB24, PIX_FMT_BGR24 + +static enum PixelFormat yuv_pix_fmts[] = { YUV_FORMATS, PIX_FMT_NONE }; +static enum PixelFormat rgb_pix_fmts[] = { RGB_FORMATS, PIX_FMT_NONE }; +static enum PixelFormat all_pix_fmts[] = { RGB_FORMATS, YUV_FORMATS, PIX_FMT_NONE }; + +static int query_formats(AVFilterContext *ctx) +{ + LutContext *lut = ctx->priv; + + enum PixelFormat *pix_fmts = lut->is_rgb ? rgb_pix_fmts : + lut->is_yuv ? yuv_pix_fmts : all_pix_fmts; + + avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); + return 0; +} + +/** + * Clip value val in the minval - maxval range. + */ +static double clip(void *opaque, double val) +{ + LutContext *lut = opaque; + double minval = lut->var_values[VAR_MINVAL]; + double maxval = lut->var_values[VAR_MAXVAL]; + + return av_clip(val, minval, maxval); +} + +/** + * Compute gamma correction for value val, assuming the minval-maxval + * range, val is clipped to a value contained in the same interval. + */ +static double compute_gammaval(void *opaque, double gamma) +{ + LutContext *lut = opaque; + double val = lut->var_values[VAR_CLIPVAL]; + double minval = lut->var_values[VAR_MINVAL]; + double maxval = lut->var_values[VAR_MAXVAL]; + + return pow((val-minval)/(maxval-minval), gamma) * (maxval-minval)+minval; +} + +static double (* const funcs1[])(void *, double) = { + clip, + compute_gammaval, + NULL +}; + +static const char * const funcs1_names[] = { + "clip", + "gammaval", + NULL +}; + +static int config_props(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + LutContext *lut = ctx->priv; + const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format]; + int min[4], max[4]; + int val, comp, ret; + + lut->hsub = desc->log2_chroma_w; + lut->vsub = desc->log2_chroma_h; + + lut->var_values[VAR_W] = inlink->w; + lut->var_values[VAR_H] = inlink->h; + + switch (inlink->format) { + case PIX_FMT_YUV410P: + case PIX_FMT_YUV411P: + case PIX_FMT_YUV420P: + case PIX_FMT_YUV422P: + case PIX_FMT_YUV440P: + case PIX_FMT_YUV444P: + case PIX_FMT_YUVA420P: + min[Y] = min[U] = min[V] = 16; + max[Y] = 235; + max[U] = max[V] = 240; + min[A] = 0; max[A] = 255; + break; + default: + min[0] = min[1] = min[2] = min[3] = 0; + max[0] = max[1] = max[2] = max[3] = 255; + } + + lut->is_yuv = lut->is_rgb = 0; + if (ff_fmt_is_in(inlink->format, yuv_pix_fmts)) lut->is_yuv = 1; + else if (ff_fmt_is_in(inlink->format, rgb_pix_fmts)) lut->is_rgb = 1; + + if (lut->is_rgb) { + switch (inlink->format) { + case PIX_FMT_ARGB: lut->rgba_map[A] = 0; lut->rgba_map[R] = 1; lut->rgba_map[G] = 2; lut->rgba_map[B] = 3; break; + case PIX_FMT_ABGR: lut->rgba_map[A] = 0; lut->rgba_map[B] = 1; lut->rgba_map[G] = 2; lut->rgba_map[R] = 3; break; + case PIX_FMT_RGBA: + case PIX_FMT_RGB24: lut->rgba_map[R] = 0; lut->rgba_map[G] = 1; lut->rgba_map[B] = 2; lut->rgba_map[A] = 3; break; + case PIX_FMT_BGRA: + case PIX_FMT_BGR24: lut->rgba_map[B] = 0; lut->rgba_map[G] = 1; lut->rgba_map[R] = 2; lut->rgba_map[A] = 3; break; + } + lut->step = av_get_bits_per_pixel(desc) >> 3; + } + + for (comp = 0; comp < desc->nb_components; comp++) { + double res; + + /* create the parsed expression */ + ret = av_expr_parse(&lut->comp_expr[comp], lut->comp_expr_str[comp], + var_names, funcs1_names, funcs1, NULL, NULL, 0, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, + "Error when parsing the expression '%s' for the component %d.\n", + lut->comp_expr_str[comp], comp); + return AVERROR(EINVAL); + } + + /* compute the lut */ + lut->var_values[VAR_MAXVAL] = max[comp]; + lut->var_values[VAR_MINVAL] = min[comp]; + + for (val = 0; val < 256; val++) { + lut->var_values[VAR_VAL] = val; + lut->var_values[VAR_CLIPVAL] = av_clip(val, min[comp], max[comp]); + lut->var_values[VAR_NEGVAL] = + av_clip(min[comp] + max[comp] - lut->var_values[VAR_VAL], + min[comp], max[comp]); + + res = av_expr_eval(lut->comp_expr[comp], lut->var_values, lut); + if (isnan(res)) { + av_log(ctx, AV_LOG_ERROR, + "Error when evaluating the expression '%s' for the value %d for the component #%d.\n", + lut->comp_expr_str[comp], val, comp); + return AVERROR(EINVAL); + } + lut->lut[comp][val] = av_clip((int)res, min[comp], max[comp]); + av_log(ctx, AV_LOG_DEBUG, "val[%d][%d] = %d\n", comp, val, lut->lut[comp][val]); + } + } + + return 0; +} + +static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) +{ + AVFilterContext *ctx = inlink->dst; + LutContext *lut = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFilterBufferRef *inpic = inlink ->cur_buf; + AVFilterBufferRef *outpic = outlink->out_buf; + uint8_t *inrow, *outrow, *inrow0, *outrow0; + int i, j, k, plane; + + if (lut->is_rgb) { + /* packed */ + inrow0 = inpic ->data[0] + y * inpic ->linesize[0]; + outrow0 = outpic->data[0] + y * outpic->linesize[0]; + + for (i = 0; i < h; i ++) { + inrow = inrow0; + outrow = outrow0; + for (j = 0; j < inlink->w; j++) { + for (k = 0; k < lut->step; k++) + outrow[k] = lut->lut[lut->rgba_map[k]][inrow[k]]; + outrow += lut->step; + inrow += lut->step; + } + inrow0 += inpic ->linesize[0]; + outrow0 += outpic->linesize[0]; + } + } else { + /* planar */ + for (plane = 0; plane < 4 && inpic->data[plane]; plane++) { + int vsub = plane == 1 || plane == 2 ? lut->vsub : 0; + int hsub = plane == 1 || plane == 2 ? lut->hsub : 0; + + inrow = inpic ->data[plane] + (y>>vsub) * inpic ->linesize[plane]; + outrow = outpic->data[plane] + (y>>vsub) * outpic->linesize[plane]; + + for (i = 0; i < h>>vsub; i ++) { + for (j = 0; j < inlink->w>>hsub; j++) + outrow[j] = lut->lut[plane][inrow[j]]; + inrow += inpic ->linesize[plane]; + outrow += outpic->linesize[plane]; + } + } + } + + avfilter_draw_slice(outlink, y, h, slice_dir); +} + +#define DEFINE_LUT_FILTER(name_, description_, init_) \ + AVFilter avfilter_vf_##name_ = { \ + .name = #name_, \ + .description = NULL_IF_CONFIG_SMALL(description_), \ + .priv_size = sizeof(LutContext), \ + \ + .init = init_, \ + .uninit = uninit, \ + .query_formats = query_formats, \ + \ + .inputs = (AVFilterPad[]) {{ .name = "default", \ + .type = AVMEDIA_TYPE_VIDEO, \ + .draw_slice = draw_slice, \ + .config_props = config_props, \ + .min_perms = AV_PERM_READ, }, \ + { .name = NULL}}, \ + .outputs = (AVFilterPad[]) {{ .name = "default", \ + .type = AVMEDIA_TYPE_VIDEO, }, \ + { .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 From 171868e25a231ff06864e9f37400680148af10b4 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Sat, 28 May 2011 22:00:26 +0200 Subject: [PATCH 07/20] lavfi: add negate filter This filter is a simple wrapper around the LUT filter. Signed-off-by: Anton Khirnov --- doc/filters.texi | 7 ++++++- libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/avfilter.h | 2 +- libavfilter/vf_lut.c | 23 +++++++++++++++++++++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index f7aacf2e19..c71fd48b6e 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -813,7 +813,12 @@ format=rgba,lutrgb=a="maxval-minval/2" lutyuv=y=gammaval(0.5) @end example -@section noformat +@section negate + +Negate input video. + +This filter accepts an integer in input, if non-zero it negates the +alpha component (if available). The default value in input is 0. Force libavfilter not to use any of the specified pixel formats for the input to the next filter. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 9659fd692f..e2285e4d39 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -36,6 +36,7 @@ OBJS-$(CONFIG_HQDN3D_FILTER) += vf_hqdn3d.o OBJS-$(CONFIG_LUT_FILTER) += vf_lut.o OBJS-$(CONFIG_LUTRGB_FILTER) += vf_lut.o OBJS-$(CONFIG_LUTYUV_FILTER) += vf_lut.o +OBJS-$(CONFIG_NEGATE_FILTER) += vf_lut.o OBJS-$(CONFIG_NOFORMAT_FILTER) += vf_format.o OBJS-$(CONFIG_NULL_FILTER) += vf_null.o OBJS-$(CONFIG_OCV_FILTER) += vf_libopencv.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 1357322302..ba853c7178 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -57,6 +57,7 @@ void avfilter_register_all(void) REGISTER_FILTER (LUT, lut, vf); REGISTER_FILTER (LUTRGB, lutrgb, vf); REGISTER_FILTER (LUTYUV, lutyuv, vf); + REGISTER_FILTER (NEGATE, negate, vf); REGISTER_FILTER (NOFORMAT, noformat, vf); REGISTER_FILTER (NULL, null, vf); REGISTER_FILTER (OCV, ocv, vf); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index d4c03792da..66237c56b4 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -29,7 +29,7 @@ #include "libavutil/rational.h" #define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 8 +#define LIBAVFILTER_VERSION_MINOR 9 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_lut.c b/libavfilter/vf_lut.c index d7fab5e17d..7f3e913945 100644 --- a/libavfilter/vf_lut.c +++ b/libavfilter/vf_lut.c @@ -69,6 +69,7 @@ typedef struct { int is_rgb, is_yuv; int rgba_map[4]; int step; + int negate_alpha; /* only used by negate */ } LutContext; #define Y 0 @@ -369,3 +370,25 @@ DEFINE_LUT_FILTER(lutyuv, "Compute and apply a lookup table to the YUV input vid #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[64]; + + if (args) + sscanf(args, "%d", &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"); + + return init(ctx, lut_params, opaque); +} + +DEFINE_LUT_FILTER(negate, "Negate input video.", negate_init); + +#endif From ce6b6ef645f6ac17f78e937837fe8f37a1b2beaf Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Sat, 9 Jul 2011 18:13:10 +0200 Subject: [PATCH 08/20] lavfi: port boxblur filter from libmpcodecs With the following additions: * support to gray format * support to yuva420p format * parametric luma/chroma/alpha radius * consistency check on the radius values, avoid crashes with invalid values Signed-off-by: Anton Khirnov --- Changelog | 1 + configure | 1 + doc/filters.texi | 60 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/avfilter.h | 2 +- libavfilter/vf_boxblur.c | 350 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 libavfilter/vf_boxblur.c diff --git a/Changelog b/Changelog index 8374e05342..a70a3db252 100644 --- a/Changelog +++ b/Changelog @@ -51,6 +51,7 @@ easier to use. The changes are: - CELT in Ogg demuxing - VC-1 interlaced decoding - lut, lutrgb, and lutyuv filters +- boxblur filter version 0.7: diff --git a/configure b/configure index dbbb86c095..720b625237 100755 --- a/configure +++ b/configure @@ -1480,6 +1480,7 @@ udp_protocol_deps="network" # filters blackframe_filter_deps="gpl" +boxblur_filter_deps="gpl" cropdetect_filter_deps="gpl" drawtext_filter_deps="libfreetype" frei0r_filter_deps="frei0r dlopen strtok_r" diff --git a/doc/filters.texi b/doc/filters.texi index c71fd48b6e..f3f77183ca 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -183,6 +183,66 @@ threshold, and defaults to 98. @var{threshold} is the threshold below which a pixel value is considered black, and defaults to 32. +@section boxblur + +Apply boxblur algorithm to the input video. + +This filter accepts the parameters: +@var{luma_power}:@var{luma_radius}:@var{chroma_radius}:@var{chroma_power}:@var{alpha_radius}:@var{alpha_power} + +Chroma and alpha parameters are optional, if not specified they default +to the corresponding values set for @var{luma_radius} and +@var{luma_power}. + +@var{luma_radius}, @var{chroma_radius}, and @var{alpha_radius} represent +the radius in pixels of the box used for blurring the corresponding +input plane. They are expressions, and can contain the following +constants: +@table @option +@item w, h +the input width and heigth in pixels + +@item cw, ch +the input chroma image width and height in pixels + +@item hsub, vsub +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 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. + +@var{luma_power}, @var{chroma_power}, and @var{alpha_power} represent +how many times the boxblur filter is applied to the corresponding +plane. + +Some examples follow: + +@itemize + +@item +Apply a boxblur filter with luma, chroma, and alpha radius +set to 2: +@example +boxblur=2:1 +@end example + +@item +Set luma radius to 2, alpha and chroma radius to 0 +@example +boxblur=2:1:0:0:0:0 +@end example + +@item +Set luma and chroma radius to a fraction of the video dimension +@example +boxblur=min(h\,w)/10:1:min(cw\,ch)/10:1 +@end example + +@end itemize + @section copy Copy the input source unchanged to the output. Mainly useful for diff --git a/libavfilter/Makefile b/libavfilter/Makefile index e2285e4d39..44f2ab94d4 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -20,6 +20,7 @@ OBJS-$(CONFIG_ANULLSRC_FILTER) += asrc_anullsrc.o OBJS-$(CONFIG_ANULLSINK_FILTER) += asink_anullsink.o OBJS-$(CONFIG_BLACKFRAME_FILTER) += vf_blackframe.o +OBJS-$(CONFIG_BOXBLUR_FILTER) += vf_boxblur.o OBJS-$(CONFIG_COPY_FILTER) += vf_copy.o OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index ba853c7178..f9746cd53e 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -41,6 +41,7 @@ void avfilter_register_all(void) REGISTER_FILTER (ANULLSINK, anullsink, asink); REGISTER_FILTER (BLACKFRAME, blackframe, vf); + REGISTER_FILTER (BOXBLUR, boxblur, vf); REGISTER_FILTER (COPY, copy, vf); REGISTER_FILTER (CROP, crop, vf); REGISTER_FILTER (CROPDETECT, cropdetect, vf); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 66237c56b4..4ed8ed9f1d 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -29,7 +29,7 @@ #include "libavutil/rational.h" #define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 9 +#define LIBAVFILTER_VERSION_MINOR 10 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_boxblur.c b/libavfilter/vf_boxblur.c new file mode 100644 index 0000000000..a314f5c2c1 --- /dev/null +++ b/libavfilter/vf_boxblur.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2002 Michael Niedermayer + * Copyright (c) 2011 Stefano Sabatini + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with Libav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * @file + * Apply a boxblur filter to the input video. + * Ported from MPlayer libmpcodecs/vf_boxblur.c. + */ + +#include "libavutil/avstring.h" +#include "libavutil/eval.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" + +static const char *var_names[] = { + "w", + "h", + "cw", + "ch", + "hsub", + "vsub", + NULL +}; + +enum var_name { + VAR_W, + VAR_H, + VAR_CW, + VAR_CH, + VAR_HSUB, + VAR_VSUB, + VARS_NB +}; + +typedef struct { + int radius; + int power; +} FilterParam; + +typedef struct { + FilterParam luma_param; + FilterParam chroma_param; + FilterParam alpha_param; + char luma_radius_expr [256]; + char chroma_radius_expr[256]; + char alpha_radius_expr [256]; + + int hsub, vsub; + int radius[4]; + int power[4]; + uint8_t *temp[2]; ///< temporary buffer used in blur_power() +} BoxBlurContext; + +#define Y 0 +#define U 1 +#define V 2 +#define A 3 + +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + BoxBlurContext *boxblur = ctx->priv; + int e; + + if (!args) { + av_log(ctx, AV_LOG_ERROR, + "Filter expects 2 or 4 or 6 arguments, none provided\n"); + return AVERROR(EINVAL); + } + + e = sscanf(args, "%255[^:]:%d:%255[^:]:%d:%255[^:]:%d", + boxblur->luma_radius_expr, &boxblur->luma_param .power, + boxblur->chroma_radius_expr, &boxblur->chroma_param.power, + boxblur->alpha_radius_expr, &boxblur->alpha_param .power); + + if (e != 2 && e != 4 && e != 6) { + av_log(ctx, AV_LOG_ERROR, + "Filter expects 2 or 4 or 6 params, provided %d\n", e); + return AVERROR(EINVAL); + } + + if (e < 4) { + boxblur->chroma_param.power = boxblur->luma_param.power; + av_strlcpy(boxblur->chroma_radius_expr, boxblur->luma_radius_expr, + sizeof(boxblur->chroma_radius_expr)); + } + if (e < 6) { + boxblur->alpha_param.power = boxblur->luma_param.power; + av_strlcpy(boxblur->alpha_radius_expr, boxblur->luma_radius_expr, + sizeof(boxblur->alpha_radius_expr)); + } + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + BoxBlurContext *boxblur = ctx->priv; + + av_freep(&boxblur->temp[0]); + av_freep(&boxblur->temp[1]); +} + +static int query_formats(AVFilterContext *ctx) +{ + enum PixelFormat pix_fmts[] = { + PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV420P, + PIX_FMT_YUV411P, PIX_FMT_YUV410P, PIX_FMT_YUVA420P, + PIX_FMT_YUV440P, PIX_FMT_GRAY8, + PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P, + PIX_FMT_YUVJ440P, + PIX_FMT_NONE + }; + + avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); + return 0; +} + +static int config_input(AVFilterLink *inlink) +{ + const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format]; + AVFilterContext *ctx = inlink->dst; + BoxBlurContext *boxblur = ctx->priv; + int w = inlink->w, h = inlink->h; + int cw, ch; + double var_values[VARS_NB], res; + char *expr; + int ret; + + av_freep(&boxblur->temp[0]); + av_freep(&boxblur->temp[1]); + if (!(boxblur->temp[0] = av_malloc(FFMAX(w, h)))) + return AVERROR(ENOMEM); + if (!(boxblur->temp[1] = av_malloc(FFMAX(w, h)))) { + av_freep(&boxblur->temp[0]); + return AVERROR(ENOMEM); + } + + boxblur->hsub = desc->log2_chroma_w; + boxblur->vsub = desc->log2_chroma_h; + + var_values[VAR_W] = inlink->w; + var_values[VAR_H] = inlink->h; + var_values[VAR_CW] = cw = w>>boxblur->hsub; + var_values[VAR_CH] = ch = h>>boxblur->vsub; + var_values[VAR_HSUB] = 1<hsub; + var_values[VAR_VSUB] = 1<vsub; + +#define EVAL_RADIUS_EXPR(comp) \ + expr = boxblur->comp##_radius_expr; \ + ret = av_expr_parse_and_eval(&res, expr, var_names, var_values, \ + NULL, NULL, NULL, NULL, NULL, 0, ctx); \ + boxblur->comp##_param.radius = res; \ + if (ret < 0) { \ + av_log(NULL, AV_LOG_ERROR, \ + "Error when evaluating " #comp " radius expression '%s'\n", expr); \ + return ret; \ + } + EVAL_RADIUS_EXPR(luma); + EVAL_RADIUS_EXPR(chroma); + EVAL_RADIUS_EXPR(alpha); + + av_log(ctx, AV_LOG_DEBUG, + "luma_radius:%d luma_power:%d " + "chroma_radius:%d chroma_power:%d " + "alpha_radius:%d alpha_power:%d " + "w:%d chroma_w:%d h:%d chroma_h:%d\n", + boxblur->luma_param .radius, boxblur->luma_param .power, + boxblur->chroma_param.radius, boxblur->chroma_param.power, + boxblur->alpha_param .radius, boxblur->alpha_param .power, + w, cw, h, ch); + +#define CHECK_RADIUS_VAL(w_, h_, comp) \ + if (boxblur->comp##_param.radius < 0 || \ + 2*boxblur->comp##_param.radius > FFMIN(w_, h_)) { \ + av_log(ctx, AV_LOG_ERROR, \ + "Invalid " #comp " radius value %d, must be >= 0 and <= %d\n", \ + boxblur->comp##_param.radius, FFMIN(w_, h_)/2); \ + return AVERROR(EINVAL); \ + } + CHECK_RADIUS_VAL(w, h, luma); + CHECK_RADIUS_VAL(cw, ch, chroma); + CHECK_RADIUS_VAL(w, h, alpha); + + boxblur->radius[Y] = boxblur->luma_param.radius; + boxblur->radius[U] = boxblur->radius[V] = boxblur->chroma_param.radius; + boxblur->radius[A] = boxblur->alpha_param.radius; + + boxblur->power[Y] = boxblur->luma_param.power; + boxblur->power[U] = boxblur->power[V] = boxblur->chroma_param.power; + boxblur->power[A] = boxblur->alpha_param.power; + + return 0; +} + +static inline void blur(uint8_t *dst, int dst_step, const uint8_t *src, int src_step, + int len, int radius) +{ + /* Naive boxblur would sum source pixels from x-radius .. x+radius + * for destination pixel x. That would be O(radius*width). + * If you now look at what source pixels represent 2 consecutive + * output pixels, then you see they are almost identical and only + * differ by 2 pixels, like: + * src0 111111111 + * dst0 1 + * src1 111111111 + * dst1 1 + * src0-src1 1 -1 + * so when you know one output pixel you can find the next by just adding + * and subtracting 1 input pixel. + * The following code adopts this faster variant. + */ + const int length = radius*2 + 1; + const int inv = ((1<<16) + length/2)/length; + int x, sum = 0; + + for (x = 0; x < radius; x++) + sum += src[x*src_step]<<1; + sum += src[radius*src_step]; + + for (x = 0; x <= radius; x++) { + sum += src[(radius+x)*src_step] - src[(radius-x)*src_step]; + dst[x*dst_step] = (sum*inv + (1<<15))>>16; + } + + for (; x < len-radius; x++) { + sum += src[(radius+x)*src_step] - src[(x-radius-1)*src_step]; + dst[x*dst_step] = (sum*inv + (1<<15))>>16; + } + + for (; x < len; x++) { + sum += src[(2*len-radius-x-1)*src_step] - src[(x-radius-1)*src_step]; + dst[x*dst_step] = (sum*inv + (1<<15))>>16; + } +} + +static inline void blur_power(uint8_t *dst, int dst_step, const uint8_t *src, int src_step, + int len, int radius, int power, uint8_t *temp[2]) +{ + uint8_t *a = temp[0], *b = temp[1]; + + if (radius && power) { + blur(a, 1, src, src_step, len, radius); + for (; power > 2; power--) { + uint8_t *c; + blur(b, 1, a, 1, len, radius); + c = a; a = b; b = c; + } + if (power > 1) { + blur(dst, dst_step, a, 1, len, radius); + } else { + int i; + for (i = 0; i < len; i++) + dst[i*dst_step] = a[i]; + } + } else { + int i; + for (i = 0; i < len; i++) + dst[i*dst_step] = src[i*src_step]; + } +} + +static void hblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize, + int w, int h, int radius, int power, uint8_t *temp[2]) +{ + int y; + + if (radius == 0 && dst == src) + return; + + for (y = 0; y < h; y++) + blur_power(dst + y*dst_linesize, 1, src + y*src_linesize, 1, + w, radius, power, temp); +} + +static void vblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize, + int w, int h, int radius, int power, uint8_t *temp[2]) +{ + int x; + + if (radius == 0 && dst == src) + return; + + for (x = 0; x < w; x++) + blur_power(dst + x, dst_linesize, src + x, src_linesize, + h, radius, power, temp); +} + +static void draw_slice(AVFilterLink *inlink, int y0, int h0, int slice_dir) +{ + AVFilterContext *ctx = inlink->dst; + BoxBlurContext *boxblur = ctx->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFilterBufferRef *inpicref = inlink ->cur_buf; + AVFilterBufferRef *outpicref = outlink->out_buf; + int plane; + int cw = inlink->w >> boxblur->hsub, ch = h0 >> boxblur->vsub; + int w[4] = { inlink->w, cw, cw, inlink->w }; + int h[4] = { h0, ch, ch, h0 }; + + for (plane = 0; inpicref->data[plane] && plane < 4; plane++) + hblur(outpicref->data[plane], outpicref->linesize[plane], + inpicref ->data[plane], inpicref ->linesize[plane], + w[plane], h[plane], boxblur->radius[plane], boxblur->power[plane], + boxblur->temp); + + for (plane = 0; inpicref->data[plane] && plane < 4; plane++) + vblur(outpicref->data[plane], outpicref->linesize[plane], + outpicref->data[plane], outpicref->linesize[plane], + w[plane], h[plane], boxblur->radius[plane], boxblur->power[plane], + boxblur->temp); + + avfilter_draw_slice(outlink, y0, h0, slice_dir); +} + +AVFilter avfilter_vf_boxblur = { + .name = "boxblur", + .description = NULL_IF_CONFIG_SMALL("Blur the input."), + .priv_size = sizeof(BoxBlurContext), + .init = init, + .uninit = uninit, + .query_formats = query_formats, + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .draw_slice = draw_slice, + .min_perms = AV_PERM_READ }, + { .name = NULL}}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, }, + { .name = NULL}}, +}; From b157be1f383e55f86324dae11e90a7b59173eec1 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Wed, 10 Aug 2011 18:58:49 +0200 Subject: [PATCH 09/20] lavfi: port libmpcodecs delogo filter The ported filter supports named option parsing and more YUV formats. Signed-off-by: Anton Khirnov --- configure | 1 + doc/filters.texi | 52 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/avfilter.h | 2 +- libavfilter/vf_delogo.c | 284 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 libavfilter/vf_delogo.c diff --git a/configure b/configure index 720b625237..a036873682 100755 --- a/configure +++ b/configure @@ -1482,6 +1482,7 @@ udp_protocol_deps="network" blackframe_filter_deps="gpl" boxblur_filter_deps="gpl" cropdetect_filter_deps="gpl" +delogo_filter_deps="gpl" drawtext_filter_deps="libfreetype" frei0r_filter_deps="frei0r dlopen strtok_r" frei0r_src_filter_deps="frei0r dlopen strtok_r" diff --git a/doc/filters.texi b/doc/filters.texi index f3f77183ca..56f00ff4e1 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -381,6 +381,58 @@ indicates never reset and return the largest area encountered during playback. @end table +@section delogo + +Suppress a TV station logo by a simple interpolation of the surrounding +pixels. Just set a rectangle covering the logo and watch it disappear +(and sometimes something even uglier appear - your mileage may vary). + +The filter accepts parameters as a string of the form +"@var{x}:@var{y}:@var{w}:@var{h}:@var{band}", or as a list of +@var{key}=@var{value} pairs, separated by ":". + +The description of the accepted parameters follows. + +@table @option + +@item x, y +Specify the top left corner coordinates of the logo. They must be +specified. + +@item w, h +Specify the width and height of the logo to clear. They must be +specified. + +@item band, t +Specify the thickness of the fuzzy edge of the rectangle (added to +@var{w} and @var{h}). The default value is 4. + +@item show +When set to 1, a green rectangle is drawn on the screen to simplify +finding the right @var{x}, @var{y}, @var{w}, @var{h} parameters, and +@var{band} is set to 4. The default value is 0. + +@end table + +Some examples follow. + +@itemize + +@item +Set a rectangle covering the area with top left corner coordinates 0,0 +and size 100x77, setting a band of size 10: +@example +delogo=0:0:100:77:10 +@end example + +@item +As the previous example, but use named options: +@example +delogo=x=0:y=0:w=100:h=77:band=10 +@end example + +@end itemize + @section drawbox Draw a colored box on the input image. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 44f2ab94d4..c5f146e949 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -24,6 +24,7 @@ OBJS-$(CONFIG_BOXBLUR_FILTER) += vf_boxblur.o OBJS-$(CONFIG_COPY_FILTER) += vf_copy.o OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o +OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o OBJS-$(CONFIG_FADE_FILTER) += vf_fade.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index f9746cd53e..f6c60a1eea 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -45,6 +45,7 @@ void avfilter_register_all(void) REGISTER_FILTER (COPY, copy, vf); REGISTER_FILTER (CROP, crop, vf); REGISTER_FILTER (CROPDETECT, cropdetect, vf); + REGISTER_FILTER (DELOGO, delogo, vf); REGISTER_FILTER (DRAWBOX, drawbox, vf); REGISTER_FILTER (DRAWTEXT, drawtext, vf); REGISTER_FILTER (FADE, fade, vf); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 4ed8ed9f1d..72b5dfa12b 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -29,7 +29,7 @@ #include "libavutil/rational.h" #define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 10 +#define LIBAVFILTER_VERSION_MINOR 11 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_delogo.c b/libavfilter/vf_delogo.c new file mode 100644 index 0000000000..ca3156814b --- /dev/null +++ b/libavfilter/vf_delogo.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2002 Jindrich Makovicka + * Copyright (c) 2011 Stefano Sabatini + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with Libav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * @file + * A very simple tv station logo remover + * Ported from MPlayer libmpcodecs/vf_delogo.c. + */ + +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" + +/** + * Apply a simple delogo algorithm to the image in dst and put the + * result in src. + * + * The algorithm is only applied to the region specified by the logo + * parameters. + * + * @param w width of the input image + * @param h height of the input image + * @param logo_x x coordinate of the top left corner of the logo region + * @param logo_y y coordinate of the top left corner of the logo region + * @param logo_w width of the logo + * @param logo_h height of the logo + * @param band the size of the band around the processed area + * @param show show a rectangle around the processed area, useful for + * parameters tweaking + * @param direct if non-zero perform in-place processing + */ +static void apply_delogo(uint8_t *dst, int dst_linesize, + uint8_t *src, int src_linesize, + int w, int h, + int logo_x, int logo_y, int logo_w, int logo_h, + int band, int show, int direct) +{ + int x, y; + int interp, dist; + uint8_t *xdst, *xsrc; + + uint8_t *topleft, *botleft, *topright; + int xclipl, xclipr, yclipt, yclipb; + int logo_x1, logo_x2, logo_y1, logo_y2; + + xclipl = FFMAX(-logo_x, 0); + xclipr = FFMAX(logo_x+logo_w-w, 0); + yclipt = FFMAX(-logo_y, 0); + yclipb = FFMAX(logo_y+logo_h-h, 0); + + logo_x1 = logo_x + xclipl; + logo_x2 = logo_x + logo_w - xclipr; + logo_y1 = logo_y + yclipt; + logo_y2 = logo_y + logo_h - yclipb; + + topleft = src+logo_y1 * src_linesize+logo_x1; + topright = src+logo_y1 * src_linesize+logo_x2-1; + botleft = src+(logo_y2-1) * src_linesize+logo_x1; + + dst += (logo_y1+1)*dst_linesize; + src += (logo_y1+1)*src_linesize; + + if (!direct) + av_image_copy_plane(dst, dst_linesize, src, src_linesize, w, h); + + for (y = logo_y1+1; y < logo_y2-1; y++) { + for (x = logo_x1+1, + xdst = dst+logo_x1+1, + xsrc = src+logo_x1+1; x < logo_x2-1; x++, xdst++, xsrc++) { + interp = (topleft[src_linesize*(y-logo_y -yclipt)] + + topleft[src_linesize*(y-logo_y-1-yclipt)] + + topleft[src_linesize*(y-logo_y+1-yclipt)]) * (logo_w-(x-logo_x))/logo_w + + (topright[src_linesize*(y-logo_y-yclipt)] + + topright[src_linesize*(y-logo_y-1-yclipt)] + + topright[src_linesize*(y-logo_y+1-yclipt)]) * (x-logo_x)/logo_w + + (topleft[x-logo_x-xclipl] + + topleft[x-logo_x-1-xclipl] + + topleft[x-logo_x+1-xclipl]) * (logo_h-(y-logo_y))/logo_h + + (botleft[x-logo_x-xclipl] + + botleft[x-logo_x-1-xclipl] + + botleft[x-logo_x+1-xclipl]) * (y-logo_y)/logo_h; + interp /= 6; + + if (y >= logo_y+band && y < logo_y+logo_h-band && + x >= logo_x+band && x < logo_x+logo_w-band) { + *xdst = interp; + } else { + dist = 0; + if (x < logo_x+band) + dist = FFMAX(dist, logo_x-x+band); + else if (x >= logo_x+logo_w-band) + dist = FFMAX(dist, x-(logo_x+logo_w-1-band)); + + if (y < logo_y+band) + dist = FFMAX(dist, logo_y-y+band); + else if (y >= logo_y+logo_h-band) + dist = FFMAX(dist, y-(logo_y+logo_h-1-band)); + + *xdst = (*xsrc*dist + interp*(band-dist))/band; + if (show && (dist == band-1)) + *xdst = 0; + } + } + + dst += dst_linesize; + src += src_linesize; + } +} + +typedef struct { + const AVClass *class; + int x, y, w, h, band, show; +} DelogoContext; + +#define OFFSET(x) offsetof(DelogoContext, x) + +static const AVOption delogo_options[]= { + {"x", "set logo x position", OFFSET(x), FF_OPT_TYPE_INT, {-1}, -1, INT_MAX }, + {"y", "set logo y position", OFFSET(y), FF_OPT_TYPE_INT, {-1}, -1, INT_MAX }, + {"w", "set logo width", OFFSET(w), FF_OPT_TYPE_INT, {-1}, -1, INT_MAX }, + {"h", "set logo height", OFFSET(h), FF_OPT_TYPE_INT, {-1}, -1, INT_MAX }, + {"band", "set delogo area band size", OFFSET(band), FF_OPT_TYPE_INT, { 4}, -1, INT_MAX }, + {"t", "set delogo area band size", OFFSET(band), FF_OPT_TYPE_INT, { 4}, -1, INT_MAX }, + {"show", "show delogo area", OFFSET(show), FF_OPT_TYPE_INT, { 0}, 0, 1 }, + {NULL}, +}; + +static const char *delogo_get_name(void *ctx) +{ + return "delogo"; +} + +static const AVClass delogo_class = { + .class_name = "DelogoContext", + .item_name = delogo_get_name, + .option = delogo_options, +}; + +static int query_formats(AVFilterContext *ctx) +{ + enum PixelFormat pix_fmts[] = { + PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV420P, + PIX_FMT_YUV411P, PIX_FMT_YUV410P, PIX_FMT_YUV440P, + PIX_FMT_YUVA420P, PIX_FMT_GRAY8, + PIX_FMT_NONE + }; + + avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); + return 0; +} + +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + DelogoContext *delogo = ctx->priv; + int ret = 0; + + delogo->class = &delogo_class; + av_opt_set_defaults(delogo); + + if (args) + ret = sscanf(args, "%d:%d:%d:%d:%d", + &delogo->x, &delogo->y, &delogo->w, &delogo->h, &delogo->band); + if (ret == 5) { + if (delogo->band < 0) + delogo->show = 1; + } else if ((ret = (av_set_options_string(delogo, args, "=", ":"))) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args); + return ret; + } + +#define CHECK_UNSET_OPT(opt) \ + if (delogo->opt == -1) { \ + av_log(delogo, AV_LOG_ERROR, "Option %s was not set.\n", #opt); \ + return AVERROR(EINVAL); \ + } + CHECK_UNSET_OPT(x); + CHECK_UNSET_OPT(y); + CHECK_UNSET_OPT(w); + CHECK_UNSET_OPT(h); + + if (delogo->show) + delogo->band = 4; + + av_log(ctx, AV_LOG_DEBUG, "x:%d y:%d, w:%d h:%d band:%d show:%d\n", + delogo->x, delogo->y, delogo->w, delogo->h, delogo->band, delogo->show); + + delogo->w += delogo->band*2; + delogo->h += delogo->band*2; + delogo->x -= delogo->band; + delogo->y -= delogo->band; + + return 0; +} + +static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref) +{ + AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFilterBufferRef *outpicref; + + if (inpicref->perms & AV_PERM_PRESERVE) { + outpicref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, + outlink->w, outlink->h); + avfilter_copy_buffer_ref_props(outpicref, inpicref); + outpicref->video->w = outlink->w; + outpicref->video->h = outlink->h; + } else + outpicref = inpicref; + + outlink->out_buf = outpicref; + avfilter_start_frame(outlink, avfilter_ref_buffer(outpicref, ~0)); +} + +static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { } + +static void end_frame(AVFilterLink *inlink) +{ + DelogoContext *delogo = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFilterBufferRef *inpicref = inlink ->cur_buf; + AVFilterBufferRef *outpicref = outlink->out_buf; + int direct = inpicref == outpicref; + int hsub0 = av_pix_fmt_descriptors[inlink->format].log2_chroma_w; + int vsub0 = av_pix_fmt_descriptors[inlink->format].log2_chroma_h; + int plane; + + for (plane = 0; plane < 4 && inpicref->data[plane]; plane++) { + int hsub = plane == 1 || plane == 2 ? hsub0 : 0; + int vsub = plane == 1 || plane == 2 ? vsub0 : 0; + + apply_delogo(outpicref->data[plane], outpicref->linesize[plane], + inpicref ->data[plane], inpicref ->linesize[plane], + inlink->w>>hsub, inlink->h>>vsub, + delogo->x>>hsub, delogo->y>>vsub, + delogo->w>>hsub, delogo->h>>vsub, + delogo->band>>FFMIN(hsub, vsub), + delogo->show, direct); + } + + avfilter_draw_slice(outlink, 0, inlink->h, 1); + avfilter_end_frame(outlink); + avfilter_unref_buffer(inpicref); + if (!direct) + avfilter_unref_buffer(outpicref); +} + +AVFilter avfilter_vf_delogo = { + .name = "delogo", + .description = NULL_IF_CONFIG_SMALL("Remove logo from input video."), + .priv_size = sizeof(DelogoContext), + .init = init, + .query_formats = query_formats, + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer = avfilter_null_get_video_buffer, + .start_frame = start_frame, + .draw_slice = null_draw_slice, + .end_frame = end_frame, + .min_perms = AV_PERM_WRITE | AV_PERM_READ, + .rej_perms = AV_PERM_PRESERVE }, + { .name = NULL}}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, }, + { .name = NULL}}, +}; From 17fff881e796980e2b9817f195e80fe2f2fd197a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Fri, 24 Jun 2011 02:04:54 +0300 Subject: [PATCH 10/20] rtsp: Merge the AVOption lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This eases adding options that are common for both. The AV_OPT_FLAG_EN/DECODING_PARAM still indicates whether they belong to the muxer or demuxer. Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 7 +++++++ libavformat/rtsp.h | 3 +++ libavformat/rtspdec.c | 8 +------- libavformat/rtspenc.c | 9 +-------- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 674a8b6017..49c148c8e1 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,12 @@ #define SDP_MAX_SIZE 16384 #define RECVBUF_SIZE 10 * RTP_MAX_PACKET_LENGTH +const AVOption ff_rtsp_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 }, + FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags), + { NULL }, +}; + static void get_word_until_chars(char *buf, int buf_size, const char *sep, const char **pp) { diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 62ca483d9d..5327b00679 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. @@ -537,4 +538,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 ccabca9377..c453b82195 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 faf76c0e19..e4e79cad03 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, }; From 2c9aa0247d6a5654d955fb018a84e7f2ec3a355e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Thu, 13 Oct 2011 14:00:06 +0300 Subject: [PATCH 11/20] rtsp: Simplify AVOption definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use defines for shortening common parts, omit the .dbl named initializer (since it's the first element in the union). Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 49c148c8e1..e4c4c98383 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -57,8 +57,10 @@ #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 const AVOption ff_rtsp_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 }, + { "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), { NULL }, }; From eca4850c6d2e8074e76234060991f490f1cff950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Fri, 24 Jun 2011 01:49:09 +0300 Subject: [PATCH 12/20] rtsp: Accept options via private avoptions instead of URL options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eventually, the old way of passing options by adding stuff to the URL can be dropped. This avoids having to tamper with the user-specified URL to pass options on the transport mode. This also works better with redirects, since the options don't need to be parsed out from the URL. Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 29 ++++++++++++++++++++++++++++- libavformat/rtsp.h | 14 +++++++++++++- libavformat/version.h | 5 ++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index e4c4c98383..9dbdcc0add 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -59,9 +59,17 @@ #define OFFSET(x) offsetof(RTSPState, x) #define DEC AV_OPT_FLAG_DECODING_PARAM +#define ENC AV_OPT_FLAG_ENCODING_PARAM 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_flags", "RTSP flags", 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" }, { NULL }, }; @@ -1317,8 +1325,19 @@ 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; + if (rt->rtsp_flags & RTSP_FLAG_FILTER_SRC) + rt->filter_source = 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); @@ -1328,6 +1347,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) { @@ -1335,6 +1355,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, '&'); @@ -1360,10 +1381,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; diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 5327b00679..a03ad9d2fe 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -38,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 */ }; /** @@ -350,8 +353,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 diff --git a/libavformat/version.h b/libavformat/version.h index 2bebed9799..db20bd6658 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -25,7 +25,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 53 #define LIBAVFORMAT_VERSION_MINOR 9 -#define LIBAVFORMAT_VERSION_MICRO 0 +#define LIBAVFORMAT_VERSION_MICRO 1 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ @@ -92,5 +92,8 @@ #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 */ From 9867aea5242b72ef182c1ffdbb08940a101f359b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Fri, 24 Jun 2011 01:55:00 +0300 Subject: [PATCH 13/20] rtsp: Remove the separate filter_source variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read it as a flag from the flags field instead. Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 6 ++---- libavformat/rtsp.h | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 9dbdcc0add..afd8e771e1 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -1235,7 +1235,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]) { @@ -1333,8 +1333,6 @@ int ff_rtsp_connect(AVFormatContext *s) } /* Only pass through valid flags from here */ rt->lower_transport_mask &= (1 << RTSP_LOWER_TRANSPORT_NB) - 1; - if (rt->rtsp_flags & RTSP_FLAG_FILTER_SRC) - rt->filter_source = 1; redirect: lower_transport_mask = rt->lower_transport_mask; @@ -1373,7 +1371,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. */ diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index a03ad9d2fe..7143acbe4f 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -317,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 */ From 02a1a28c611aff53db6fc93f46bed2453019a589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Wed, 12 Oct 2011 18:27:18 +0300 Subject: [PATCH 14/20] doc: Update the documentation on setting options for RTSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Storsjö --- doc/protocols.texi | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/doc/protocols.texi b/doc/protocols.texi index cc35982e77..8aee5f639c 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{avconv}/@file{avplay} 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 -avplay -max_delay 500000 rtsp://server/video.mp4?udp +avplay -max_delay 500000 -rtsp_transport udp rtsp://server/video.mp4 @end example To watch a stream tunneled over HTTP: @example -avplay rtsp://server/video.mp4?http +avplay -rtsp_transport http rtsp://server/video.mp4 @end example To send a stream in realtime to a RTSP server, for others to watch: From 2e69dd66b6e47ba33f9bb6696e9c587d3057034c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Mon, 17 Oct 2011 20:24:54 +0300 Subject: [PATCH 15/20] rtp: Fix ff_rtp_get_payload_type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was broken in 3b3ea34655db02d9cd9ea1a4122e920a7fdec602 "Remove all uses of deprecated AVOptions API", where any presence of a payload_type AVOption caused its value to be returned, even if it wasn't set (and thus had the default -1 value). This caused the RTP muxer to be broken. Signed-off-by: Martin Storsjö --- libavformat/rtp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libavformat/rtp.c b/libavformat/rtp.c index 4be845a06c..b6b4b72aa3 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; } From 299234a0a9e7d96d4cfe7ec88aac007bf4a6bbff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Mon, 17 Oct 2011 20:50:02 +0300 Subject: [PATCH 16/20] avoptions: Fix av_opt_flag_is_set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the changes in 3b3ea34655db02d9cd9ea1a4122e920a7fdec602, "Remove all uses of deprecated AVOptions API", av_opt_flag_is_set was broken, since it now uses av_opt_find, which doesn't return named constants unless a unit to look for the constant in is given. This broke enabling LATM encapsulated AAC output in RTP. Signed-off-by: Martin Storsjö --- libavutil/opt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libavutil/opt.c b/libavutil/opt.c index bf63bef2af..aa76301740 100644 --- a/libavutil/opt.c +++ b/libavutil/opt.c @@ -501,7 +501,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 || From 9f938ca5e653c7d05d4ef22226eb61334a961e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Mon, 17 Oct 2011 21:11:07 +0300 Subject: [PATCH 17/20] rtpenc: Set a default video codec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit avconv doesn't map video streams to a muxer without specifying a manual stream mapping if the default video codec is CODEC_ID_NONE. Signed-off-by: Martin Storsjö --- libavformat/rtpenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index 165e4449c7..dfd7461d18 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, From 3a6765fb5d263f1b16be57d5564678147ba7f438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Mon, 17 Oct 2011 20:04:41 +0300 Subject: [PATCH 18/20] rtsp: Make the rtsp flags avoptions set via a define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helps sharing these options with the sdp and rtp demuxers. Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index afd8e771e1..4765d2f6bf 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -60,6 +60,11 @@ #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), @@ -68,8 +73,7 @@ const AVOption ff_rtsp_options[] = { { "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_flags", "RTSP flags", 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" }, + RTSP_FLAG_OPTS("rtsp_flags", "RTSP flags"), { NULL }, }; From 51369f2891f756bcca4d7283d5a24c0529c1d6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Mon, 17 Oct 2011 20:07:56 +0300 Subject: [PATCH 19/20] rtsp: Expose the flag options via private AVOptions for sdp and rtp, too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows setting the filter_src option for these demuxers, too, which wasn't possible at all before (where the option only was set via URL parameters for RTSP). Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 4765d2f6bf..678594714f 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -77,6 +77,16 @@ const AVOption ff_rtsp_options[] = { { 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) { @@ -1835,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; @@ -1858,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"), @@ -1866,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 */ @@ -1962,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"), @@ -1971,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 */ From 3ec344626c4ec2f515169f89bffe95dea92a5982 Mon Sep 17 00:00:00 2001 From: Alexandra Khirnova Date: Mon, 10 Oct 2011 11:52:13 +0100 Subject: [PATCH 20/20] avconv: add presets Signed-off-by: Anton Khirnov --- avconv.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++- doc/avconv.texi | 17 +++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/avconv.c b/avconv.c index 44bc59f0cb..3c68ffc10b 100644 --- a/avconv.c +++ b/avconv.c @@ -326,6 +326,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; @@ -2966,15 +2968,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"); @@ -2996,6 +3045,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; @@ -3906,6 +3980,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 9faaee630d..6e38a47b78 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