From d906f49aa8f36fc173cfa96580cbe29e88be5049 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 14 Sep 2011 08:55:27 +0200 Subject: [PATCH 1/7] rawdec: g722 is always 1 channel/16kHz --- libavformat/rawdec.c | 9 +++++---- tests/fate2.mak | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c index 76ab09c0e5..f0d487bd6f 100644 --- a/libavformat/rawdec.c +++ b/libavformat/rawdec.c @@ -51,9 +51,12 @@ int ff_raw_read_header(AVFormatContext *s, AVFormatParameters *ap) st->codec->channels = 1; - if (s1->sample_rate) + if (id == CODEC_ID_ADPCM_G722) + st->codec->sample_rate = 16000; + + if (s1 && s1->sample_rate) st->codec->sample_rate = s1->sample_rate; - if (s1->channels) + if (s1 && s1->channels) st->codec->channels = s1->channels; st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); @@ -199,13 +202,11 @@ const AVClass ff_rawvideo_demuxer_class = { AVInputFormat ff_g722_demuxer = { .name = "g722", .long_name = NULL_IF_CONFIG_SMALL("raw G.722"), - .priv_data_size = sizeof(RawAudioDemuxerContext), .read_header = ff_raw_read_header, .read_packet = ff_raw_read_partial_packet, .flags= AVFMT_GENERIC_INDEX, .extensions = "g722,722", .value = CODEC_ID_ADPCM_G722, - .priv_class = &ff_rawaudio_demuxer_class, }; #endif diff --git a/tests/fate2.mak b/tests/fate2.mak index 6a01412eb3..0bb6654f8d 100644 --- a/tests/fate2.mak +++ b/tests/fate2.mak @@ -131,7 +131,7 @@ FATE_TESTS += fate-gsm-ms fate-gsm-ms: CMD = framecrc -i $(SAMPLES)/gsm/ciao.wav FATE_TESTS += fate-g722dec-1 -fate-g722dec-1: CMD = framecrc -ar 16000 -i $(SAMPLES)/g722/conf-adminmenu-162.g722 +fate-g722dec-1: CMD = framecrc -i $(SAMPLES)/g722/conf-adminmenu-162.g722 FATE_TESTS += fate-msmpeg4v1 fate-msmpeg4v1: CMD = framecrc -flags +bitexact -dct fastint -idct simple -i $(SAMPLES)/msmpeg4v1/mpg4.avi -an From 34d2bf30a082d489487493df40496cc18ca4300b Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 14 Sep 2011 09:54:29 +0200 Subject: [PATCH 2/7] pcmdec: use unique classes for all pcm demuxers. --- libavformat/pcmdec.c | 16 +++++++++++++++- libavformat/rawdec.c | 13 ------------- libavformat/rawdec.h | 1 - 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libavformat/pcmdec.c b/libavformat/pcmdec.c index 343bbf0ae2..90799dd664 100644 --- a/libavformat/pcmdec.c +++ b/libavformat/pcmdec.c @@ -22,6 +22,8 @@ #include "avformat.h" #include "rawdec.h" #include "pcm.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" #define RAW_SAMPLES 1024 @@ -46,7 +48,19 @@ static int raw_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +static const AVOption pcm_options[] = { + { "sample_rate", "", offsetof(RawAudioDemuxerContext, sample_rate), FF_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { "channels", "", offsetof(RawAudioDemuxerContext, channels), FF_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + #define PCMDEF(name, long_name, ext, codec) \ +static const AVClass name ## _demuxer_class = {\ + .class_name = #name " demuxer",\ + .item_name = av_default_item_name,\ + .option = pcm_options,\ + .version = LIBAVUTIL_VERSION_INT,\ +};\ AVInputFormat ff_pcm_ ## name ## _demuxer = {\ #name,\ NULL_IF_CONFIG_SMALL(long_name),\ @@ -59,7 +73,7 @@ AVInputFormat ff_pcm_ ## name ## _demuxer = {\ .flags= AVFMT_GENERIC_INDEX,\ .extensions = ext,\ .value = codec,\ - .priv_class = &ff_rawaudio_demuxer_class,\ + .priv_class = &name ## _demuxer_class,\ }; PCMDEF(f64be, "PCM 64 bit floating-point big-endian format", diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c index f0d487bd6f..0b9a0824ff 100644 --- a/libavformat/rawdec.c +++ b/libavformat/rawdec.c @@ -167,19 +167,6 @@ fail: /* Note: Do not forget to add new entries to the Makefile as well. */ -static const AVOption audio_options[] = { - { "sample_rate", "", offsetof(RawAudioDemuxerContext, sample_rate), FF_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, - { "channels", "", offsetof(RawAudioDemuxerContext, channels), FF_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, - { NULL }, -}; - -const AVClass ff_rawaudio_demuxer_class = { - .class_name = "rawaudio demuxer", - .item_name = av_default_item_name, - .option = audio_options, - .version = LIBAVUTIL_VERSION_INT, -}; - #define OFFSET(x) offsetof(FFRawVideoDemuxerContext, x) #define DEC AV_OPT_FLAG_DECODING_PARAM static const AVOption video_options[] = { diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h index 76e8053f6d..73bfb4abc4 100644 --- a/libavformat/rawdec.h +++ b/libavformat/rawdec.h @@ -38,7 +38,6 @@ typedef struct FFRawVideoDemuxerContext { char *framerate; /**< String describing framerate, set by a private option. */ } FFRawVideoDemuxerContext; -extern const AVClass ff_rawaudio_demuxer_class; extern const AVClass ff_rawvideo_demuxer_class; int ff_raw_read_header(AVFormatContext *s, AVFormatParameters *ap); From 85d982f1e2c3efb8d5622caea162cede3f6c0e21 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 14 Sep 2011 14:03:55 +0200 Subject: [PATCH 3/7] rawdec: refactor private option for raw video demuxers pixel_format/video_size only apply to 'rawvideo' (==uncompressed) demuxer and make no sense for the other raw (== containerless) demuxers. Keep only the framerate option for those. Also use unique classes for all raw video demuxers --- libavformat/ingenientdec.c | 4 +++- libavformat/rawdec.c | 15 ++------------- libavformat/rawdec.h | 14 ++++++++++++-- libavformat/rawvideodec.c | 18 +++++++++++++++++- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/libavformat/ingenientdec.c b/libavformat/ingenientdec.c index 50a4357b79..35ac649695 100644 --- a/libavformat/ingenientdec.c +++ b/libavformat/ingenientdec.c @@ -58,6 +58,8 @@ static int ingenient_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +FF_RAWVIDEO_DEMUXER_CLASS(ingenient) + AVInputFormat ff_ingenient_demuxer = { .name = "ingenient", .long_name = NULL_IF_CONFIG_SMALL("raw Ingenient MJPEG"), @@ -67,5 +69,5 @@ AVInputFormat ff_ingenient_demuxer = { .flags= AVFMT_GENERIC_INDEX, .extensions = "cgi", // FIXME .value = CODEC_ID_MJPEG, - .priv_class = &ff_rawvideo_demuxer_class, + .priv_class = &ingenient_demuxer_class, }; diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c index 0b9a0824ff..35df5e2462 100644 --- a/libavformat/rawdec.c +++ b/libavformat/rawdec.c @@ -169,21 +169,10 @@ fail: #define OFFSET(x) offsetof(FFRawVideoDemuxerContext, x) #define DEC AV_OPT_FLAG_DECODING_PARAM -static const AVOption video_options[] = { - { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), FF_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, - { "pixel_format", "", OFFSET(pixel_format), FF_OPT_TYPE_STRING, {.str = "yuv420p"}, 0, 0, DEC }, - { "framerate", "", OFFSET(framerate), FF_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC }, +const AVOption ff_rawvideo_options[] = { + { "framerate", "", OFFSET(framerate), FF_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC}, { NULL }, }; -#undef OFFSET -#undef DEC - -const AVClass ff_rawvideo_demuxer_class = { - .class_name = "rawvideo demuxer", - .item_name = av_default_item_name, - .option = video_options, - .version = LIBAVUTIL_VERSION_INT, -}; #if CONFIG_G722_DEMUXER AVInputFormat ff_g722_demuxer = { diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h index 73bfb4abc4..136f6c2d48 100644 --- a/libavformat/rawdec.h +++ b/libavformat/rawdec.h @@ -24,6 +24,7 @@ #include "avformat.h" #include "libavutil/log.h" +#include "libavutil/opt.h" typedef struct RawAudioDemuxerContext { AVClass *class; @@ -38,7 +39,7 @@ typedef struct FFRawVideoDemuxerContext { char *framerate; /**< String describing framerate, set by a private option. */ } FFRawVideoDemuxerContext; -extern const AVClass ff_rawvideo_demuxer_class; +extern const AVOption ff_rawvideo_options[]; int ff_raw_read_header(AVFormatContext *s, AVFormatParameters *ap); @@ -48,7 +49,16 @@ int ff_raw_audio_read_header(AVFormatContext *s, AVFormatParameters *ap); int ff_raw_video_read_header(AVFormatContext *s, AVFormatParameters *ap); +#define FF_RAWVIDEO_DEMUXER_CLASS(name)\ +static const AVClass name ## _demuxer_class = {\ + .class_name = #name " demuxer",\ + .item_name = av_default_item_name,\ + .option = ff_rawvideo_options,\ + .version = LIBAVUTIL_VERSION_INT,\ +}; + #define FF_DEF_RAWVIDEO_DEMUXER(shortname, longname, probe, ext, id)\ +FF_RAWVIDEO_DEMUXER_CLASS(shortname)\ AVInputFormat ff_ ## shortname ## _demuxer = {\ .name = #shortname,\ .long_name = NULL_IF_CONFIG_SMALL(longname),\ @@ -59,7 +69,7 @@ AVInputFormat ff_ ## shortname ## _demuxer = {\ .flags = AVFMT_GENERIC_INDEX,\ .value = id,\ .priv_data_size = sizeof(FFRawVideoDemuxerContext),\ - .priv_class = &ff_rawvideo_demuxer_class,\ + .priv_class = &shortname ## _demuxer_class,\ }; #endif /* AVFORMAT_RAWDEC_H */ diff --git a/libavformat/rawvideodec.c b/libavformat/rawvideodec.c index e1c52abb0a..7b9d34e38c 100644 --- a/libavformat/rawvideodec.c +++ b/libavformat/rawvideodec.c @@ -44,6 +44,22 @@ static int rawvideo_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } +#define OFFSET(x) offsetof(FFRawVideoDemuxerContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +static const AVOption rawvideo_options[] = { + { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), FF_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, + { "pixel_format", "", OFFSET(pixel_format), FF_OPT_TYPE_STRING, {.str = "yuv420p"}, 0, 0, DEC }, + { "framerate", "", OFFSET(framerate), FF_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC }, + { NULL }, +}; + +static const AVClass rawvideo_demuxer_class = { + .class_name = "rawvideo demuxer", + .item_name = av_default_item_name, + .option = rawvideo_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVInputFormat ff_rawvideo_demuxer = { .name = "rawvideo", .long_name = NULL_IF_CONFIG_SMALL("raw video format"), @@ -53,5 +69,5 @@ AVInputFormat ff_rawvideo_demuxer = { .flags= AVFMT_GENERIC_INDEX, .extensions = "yuv,cif,qcif,rgb", .value = CODEC_ID_RAWVIDEO, - .priv_class = &ff_rawvideo_demuxer_class, + .priv_class = &rawvideo_demuxer_class, }; From e417d80d2853bbeab90eea073ef4fa4a3fcf9d50 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Tue, 13 Sep 2011 17:23:06 +0200 Subject: [PATCH 4/7] lavd: add libcdio-paranoia input device for audio CD grabbing --- Changelog | 1 + configure | 8 ++ libavdevice/Makefile | 1 + libavdevice/alldevices.c | 1 + libavdevice/avdevice.h | 2 +- libavdevice/libcdio.c | 186 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 libavdevice/libcdio.c diff --git a/Changelog b/Changelog index 8a36fdd7e0..d064a2076d 100644 --- a/Changelog +++ b/Changelog @@ -45,6 +45,7 @@ easier to use. The changes are: - LATM muxer - showinfo filter - split filter +- libcdio-paranoia input device for audio CD grabbing version 0.7: diff --git a/configure b/configure index e26124e8d1..e01d9fda86 100755 --- a/configure +++ b/configure @@ -165,6 +165,7 @@ External library support: --enable-libopencore-amrnb enable AMR-NB de/encoding via libopencore-amrnb [no] --enable-libopencore-amrwb enable AMR-WB decoding via libopencore-amrwb [no] --enable-libopencv enable video filtering via libopencv [no] + --enable-libcdio enable audio CD grabbing with libcdio --enable-libdc1394 enable IIDC-1394 grabbing using libdc1394 and libraw1394 [no] --enable-libdirac enable Dirac support via libdirac [no] @@ -927,6 +928,7 @@ CONFIG_LIST=" h264pred hardcoded_tables huffman + libcdio libdc1394 libdirac libfaac @@ -1451,6 +1453,7 @@ bktr_indev_deps_any="dev_bktr_ioctl_bt848_h machine_ioctl_bt848_h dev_video_bktr dv1394_indev_deps="dv1394 dv_demuxer" fbdev_indev_deps="linux_fb_h" jack_indev_deps="jack_jack_h" +libcdio_indev_deps="libcdio" libdc1394_indev_deps="libdc1394" oss_indev_deps_any="soundcard_h sys_soundcard_h" oss_outdev_deps_any="soundcard_h sys_soundcard_h" @@ -2546,6 +2549,7 @@ die_license_disabled() { enabled $1 || { enabled $2 && die "$2 is $1 and --enable-$1 is not specified."; } } +die_license_disabled gpl libcdio die_license_disabled gpl libx264 die_license_disabled gpl libxavs die_license_disabled gpl libxvid @@ -2941,6 +2945,9 @@ enabled jack_indev && check_lib2 jack/jack.h jack_client_open -ljack enabled_any sndio_indev sndio_outdev && check_lib2 sndio.h sio_open -lsndio +enabled libcdio && + check_lib2 "cdio/cdda.h cdio/paranoia.h" cdio_cddap_open "-lcdio_paranoia -lcdio_cdda -lcdio" + enabled x11grab && check_header X11/Xlib.h && check_header X11/extensions/XShm.h && @@ -3146,6 +3153,7 @@ echo "libva enabled ${vaapi-no}" echo "libvdpau enabled ${vdpau-no}" echo "AVISynth enabled ${avisynth-no}" echo "frei0r enabled ${frei0r-no}" +echo "libcdio support ${libcdio-no}" echo "libdc1394 support ${libdc1394-no}" echo "libdirac enabled ${libdirac-no}" echo "libfaac enabled ${libfaac-no}" diff --git a/libavdevice/Makefile b/libavdevice/Makefile index eaf27ddc42..d8a5945549 100644 --- a/libavdevice/Makefile +++ b/libavdevice/Makefile @@ -24,6 +24,7 @@ OBJS-$(CONFIG_VFWCAP_INDEV) += vfwcap.o OBJS-$(CONFIG_X11_GRAB_DEVICE_INDEV) += x11grab.o # external libraries +OBJS-$(CONFIG_LIBCDIO_INDEV) += libcdio.o OBJS-$(CONFIG_LIBDC1394_INDEV) += libdc1394.o SKIPHEADERS-$(HAVE_ALSA_ASOUNDLIB_H) += alsa-audio.h diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c index e7bfb027cd..a1a333f454 100644 --- a/libavdevice/alldevices.c +++ b/libavdevice/alldevices.c @@ -54,5 +54,6 @@ void avdevice_register_all(void) REGISTER_INDEV (X11_GRAB_DEVICE, x11_grab_device); /* external libraries */ + REGISTER_INDEV (LIBCDIO, libcdio); REGISTER_INDEV (LIBDC1394, libdc1394); } diff --git a/libavdevice/avdevice.h b/libavdevice/avdevice.h index d82b26fda6..c0d392d939 100644 --- a/libavdevice/avdevice.h +++ b/libavdevice/avdevice.h @@ -22,7 +22,7 @@ #include "libavutil/avutil.h" #define LIBAVDEVICE_VERSION_MAJOR 53 -#define LIBAVDEVICE_VERSION_MINOR 0 +#define LIBAVDEVICE_VERSION_MINOR 1 #define LIBAVDEVICE_VERSION_MICRO 0 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ diff --git a/libavdevice/libcdio.c b/libavdevice/libcdio.c new file mode 100644 index 0000000000..be1c516b09 --- /dev/null +++ b/libavdevice/libcdio.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2011 Anton Khirnov + * + * 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 + * libcdio CD grabbing + */ + +#include +#include + +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" + +#include "libavformat/avformat.h" +#include "libavformat/internal.h" + +/* cdio returns some malloced strings that need to be free()d */ +#undef free + +typedef struct CDIOContext { + cdrom_drive_t *drive; + cdrom_paranoia_t *paranoia; + int32_t last_sector; + + /* private options */ + int speed; + int paranoia_mode; +} CDIOContext; + +static av_cold int read_header(AVFormatContext *ctx, AVFormatParameters *ap) +{ + CDIOContext *s = ctx->priv_data; + AVStream *st; + int ret, i; + char *err = NULL; + + if (!(st = av_new_stream(ctx, 0))) + return AVERROR(ENOMEM); + s->drive = cdio_cddap_identify(ctx->filename, CDDA_MESSAGE_LOGIT, &err); + if (!s->drive) { + av_log(ctx, AV_LOG_ERROR, "Could not open drive %s.\n", ctx->filename); + return AVERROR(EINVAL); + } + if (err) { + av_log(ctx, AV_LOG_VERBOSE, "%s\n", err); + free(err); + } + if ((ret = cdio_cddap_open(s->drive)) < 0 || !s->drive->opened) { + av_log(ctx, AV_LOG_ERROR, "Could not open disk in drive %s.\n", ctx->filename); + return AVERROR(EINVAL); + } + + cdio_cddap_verbose_set(s->drive, CDDA_MESSAGE_LOGIT, CDDA_MESSAGE_LOGIT); + if (s->speed) + cdio_cddap_speed_set(s->drive, s->speed); + + s->paranoia = cdio_paranoia_init(s->drive); + if (!s->paranoia) { + av_log(ctx, AV_LOG_ERROR, "Could not init paranoia.\n"); + return AVERROR(EINVAL); + } + cdio_paranoia_modeset(s->paranoia, s->paranoia_mode); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + if (s->drive->bigendianp) + st->codec->codec_id = CODEC_ID_PCM_S16BE; + else + st->codec->codec_id = CODEC_ID_PCM_S16LE; + st->codec->sample_rate = 44100; + st->codec->channels = 2; + if (s->drive->audio_last_sector != CDIO_INVALID_LSN && + s->drive->audio_first_sector != CDIO_INVALID_LSN) + st->duration = s->drive->audio_last_sector - s->drive->audio_first_sector; + else if (s->drive->tracks) + st->duration = s->drive->disc_toc[s->drive->tracks].dwStartSector; + av_set_pts_info(st, 64, CDIO_CD_FRAMESIZE_RAW, 2*st->codec->channels*st->codec->sample_rate); + + for (i = 0; i < s->drive->tracks; i++) { + char title[16]; + snprintf(title, sizeof(title), "track %02d", s->drive->disc_toc[i].bTrack); + ff_new_chapter(ctx, i, st->time_base, s->drive->disc_toc[i].dwStartSector, + s->drive->disc_toc[i+1].dwStartSector, title); + } + + s->last_sector = cdio_cddap_disc_lastsector(s->drive); + + return 0; +} + +static int read_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + CDIOContext *s = ctx->priv_data; + int ret; + uint16_t *buf; + char *err = NULL; + + if (ctx->streams[0]->cur_dts > s->last_sector) + return AVERROR_EOF; + + buf = cdio_paranoia_read(s->paranoia, NULL); + if (!buf) + return AVERROR_EOF; + + if (err = cdio_cddap_errors(s->drive)) { + av_log(ctx, AV_LOG_ERROR, "%s\n", err); + free(err); + err = NULL; + } + if (err = cdio_cddap_messages(s->drive)) { + av_log(ctx, AV_LOG_VERBOSE, "%s\n", err); + free(err); + err = NULL; + } + + if ((ret = av_new_packet(pkt, CDIO_CD_FRAMESIZE_RAW)) < 0) + return ret; + memcpy(pkt->data, buf, CDIO_CD_FRAMESIZE_RAW); + return 0; +} + +static av_cold int read_close(AVFormatContext *ctx) +{ + CDIOContext *s = ctx->priv_data; + cdio_paranoia_free(s->paranoia); + cdio_cddap_close(s->drive); + return 0; +} + +static int read_seek(AVFormatContext *ctx, int stream_index, int64_t timestamp, + int flags) +{ + CDIOContext *s = ctx->priv_data; + AVStream *st = ctx->streams[0]; + + cdio_paranoia_seek(s->paranoia, timestamp, SEEK_SET); + st->cur_dts = timestamp; + return 0; +} + +#define OFFSET(x) offsetof(CDIOContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "speed", "Drive reading speed.", OFFSET(speed), FF_OPT_TYPE_INT, { 0 }, 0, INT_MAX, DEC }, + { "paranoia_mode", "Error recovery mode.", OFFSET(paranoia_mode), FF_OPT_TYPE_FLAGS, { 0 }, INT_MIN, INT_MAX, DEC, "paranoia_mode" }, + { "verify", "Verify data integrity in overlap area", 0, FF_OPT_TYPE_CONST, { PARANOIA_MODE_VERIFY }, 0, 0, DEC, "paranoia_mode" }, + { "overlap", "Perform overlapped reads.", 0, FF_OPT_TYPE_CONST, { PARANOIA_MODE_OVERLAP }, 0, 0, DEC, "paranoia_mode" }, + { "neverskip", "Do not skip failed reads.", 0, FF_OPT_TYPE_CONST, { PARANOIA_MODE_NEVERSKIP }, 0, 0, DEC, "paranoia_mode" }, + { NULL }, +}; + +static const AVClass libcdio_class = { + .class_name = "libcdio indev", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_libcdio_demuxer = { + .name = "libcdio", + .read_header = read_header, + .read_packet = read_packet, + .read_close = read_close, + .read_seek = read_seek, + .priv_data_size = sizeof(CDIOContext), + .flags = AVFMT_NOFILE, + .priv_class = &libcdio_class, +}; From 7574cacbd5343bc303ee8333956274716e2444d5 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Fri, 16 Sep 2011 16:06:45 +0200 Subject: [PATCH 5/7] movenc: create an alternate group for each media type Partially fixes bug 44. --- libavformat/movenc.c | 3 ++- tests/ref/acodec/alac | 2 +- tests/ref/acodec/pcm | 8 ++++---- tests/ref/lavf/mov | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index c7029ab987..88239c5655 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -1216,7 +1216,8 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVTrack *track, AVStream *st) avio_wb32(pb, 0); /* reserved */ avio_wb32(pb, 0); /* reserved */ - avio_wb32(pb, 0x0); /* reserved (Layer & Alternate group) */ + avio_wb16(pb, 0); /* layer */ + avio_wb16(pb, st->codec->codec_type); /* alternate group) */ /* Volume, only for audio */ if(track->enc->codec_type == AVMEDIA_TYPE_AUDIO) avio_wb16(pb, 0x0100); diff --git a/tests/ref/acodec/alac b/tests/ref/acodec/alac index 1f4b264b87..35a1d8e1bf 100644 --- a/tests/ref/acodec/alac +++ b/tests/ref/acodec/alac @@ -1,4 +1,4 @@ -c68f649777ab8e7c9a0f1f221451d3ad *./tests/data/acodec/alac.m4a +b25bcc7ec3f5c19cdfc01a6bbd32edb8 *./tests/data/acodec/alac.m4a 389386 ./tests/data/acodec/alac.m4a 95e54b261530a1bcf6de6fe3b21dc5f6 *./tests/data/alac.acodec.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 diff --git a/tests/ref/acodec/pcm b/tests/ref/acodec/pcm index 033f8bc8c6..fc9dd8f29d 100644 --- a/tests/ref/acodec/pcm +++ b/tests/ref/acodec/pcm @@ -6,7 +6,7 @@ f443a8eeb1647ec1eeb8370c939e52d4 *./tests/data/acodec/pcm_mulaw.wav 529256 ./tests/data/acodec/pcm_mulaw.wav 1c3eeaa8814ebd4916780dff80ed6dc5 *./tests/data/pcm.acodec.out.wav stddev: 103.38 PSNR: 56.04 MAXDIFF: 644 bytes: 1058400/ 1058400 -b7936d7170e0efefb379349d81aed360 *./tests/data/acodec/pcm_s8.mov +760f85fb9f4e8aba326fb44ae84c9507 *./tests/data/acodec/pcm_s8.mov 530837 ./tests/data/acodec/pcm_s8.mov 652edf30f35ad89bf27bcc9d2f9c7b53 *./tests/data/pcm.acodec.out.wav stddev: 147.89 PSNR: 52.93 MAXDIFF: 255 bytes: 1058400/ 1058400 @@ -14,7 +14,7 @@ stddev: 147.89 PSNR: 52.93 MAXDIFF: 255 bytes: 1058400/ 1058400 529244 ./tests/data/acodec/pcm_u8.wav 652edf30f35ad89bf27bcc9d2f9c7b53 *./tests/data/pcm.acodec.out.wav stddev: 147.89 PSNR: 52.93 MAXDIFF: 255 bytes: 1058400/ 1058400 -c42b9c04305455250366c84e17c1023f *./tests/data/acodec/pcm_s16be.mov +a4e18d1ca9ef5b8132a84d43625ddc47 *./tests/data/acodec/pcm_s16be.mov 1060037 ./tests/data/acodec/pcm_s16be.mov 95e54b261530a1bcf6de6fe3b21dc5f6 *./tests/data/pcm.acodec.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 @@ -30,7 +30,7 @@ c4f51bf32fad2f7af8ea5beedb56168b *./tests/data/acodec/pcm_s16le.mkv 1060638 ./tests/data/acodec/pcm_s16le.mkv 95e54b261530a1bcf6de6fe3b21dc5f6 *./tests/data/pcm.acodec.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 -07ffe7ffb78f3648b6524debdde5aec1 *./tests/data/acodec/pcm_s24be.mov +971d2d2633e41a0326fe2d04a2d0350f *./tests/data/acodec/pcm_s24be.mov 1589237 ./tests/data/acodec/pcm_s24be.mov 95e54b261530a1bcf6de6fe3b21dc5f6 *./tests/data/pcm.acodec.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 @@ -38,7 +38,7 @@ a85380fb79b0d4fff38e24ac1e34bb94 *./tests/data/acodec/pcm_s24le.wav 1587668 ./tests/data/acodec/pcm_s24le.wav 95e54b261530a1bcf6de6fe3b21dc5f6 *./tests/data/pcm.acodec.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 -d7792f0343cd66fda8b50b569e2bcc48 *./tests/data/acodec/pcm_s32be.mov +fc4f4e3e195bbde037ed31021d229f12 *./tests/data/acodec/pcm_s32be.mov 2118437 ./tests/data/acodec/pcm_s32be.mov 95e54b261530a1bcf6de6fe3b21dc5f6 *./tests/data/pcm.acodec.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 diff --git a/tests/ref/lavf/mov b/tests/ref/lavf/mov index 22aac3600e..07404aa862 100644 --- a/tests/ref/lavf/mov +++ b/tests/ref/lavf/mov @@ -1,3 +1,3 @@ -439684b82ccc1fdd24a23392c238ae53 *./tests/data/lavf/lavf.mov +4a3ad13f0355cb5d119109778d555207 *./tests/data/lavf/lavf.mov 357681 ./tests/data/lavf/lavf.mov ./tests/data/lavf/lavf.mov CRC=0x2f6a9b26 From 0e7efb9d23c3641d50caa288818e8c27647ce74d Mon Sep 17 00:00:00 2001 From: Laurent Aimar Date: Sun, 11 Sep 2011 23:26:12 +0200 Subject: [PATCH 6/7] oggdec: fix out of bound write in the ogg demuxer Between ogg_save() and ogg_restore() calls, the number of streams could have been reduced. Signed-off-by: Luca Barbato --- libavformat/oggdec.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index 3d03a5f974..85f7ec758e 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -92,14 +92,24 @@ static int ogg_restore(AVFormatContext *s, int discard) ogg->state = ost->next; if (!discard){ + struct ogg_stream *old_streams = ogg->streams; + for (i = 0; i < ogg->nstreams; i++) av_free (ogg->streams[i].buf); avio_seek (bc, ost->pos, SEEK_SET); ogg->curidx = ost->curidx; ogg->nstreams = ost->nstreams; - memcpy(ogg->streams, ost->streams, - ost->nstreams * sizeof(*ogg->streams)); + ogg->streams = av_realloc (ogg->streams, + ogg->nstreams * sizeof (*ogg->streams)); + + if (ogg->streams) { + memcpy(ogg->streams, ost->streams, + ost->nstreams * sizeof(*ogg->streams)); + } else { + av_free(old_streams); + ogg->nstreams = 0; + } } av_free (ost); From d763fb7d47fdbd107ea65cdf511f2f21558f6610 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Sat, 7 May 2011 02:06:25 +0200 Subject: [PATCH 7/7] lavfi: add select filter Signed-off-by: Anton Khirnov --- Changelog | 1 + doc/filters.texi | 116 +++++++++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/avfilter.h | 2 +- libavfilter/vf_select.c | 353 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 473 insertions(+), 1 deletion(-) create mode 100644 libavfilter/vf_select.c diff --git a/Changelog b/Changelog index d064a2076d..24fa85c898 100644 --- a/Changelog +++ b/Changelog @@ -46,6 +46,7 @@ easier to use. The changes are: - showinfo filter - split filter - libcdio-paranoia input device for audio CD grabbing +- select filter version 0.7: diff --git a/doc/filters.texi b/doc/filters.texi index d8d9062bb9..a86b20e86d 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -1064,6 +1064,122 @@ scale="trunc(3/2*iw/hsub)*hsub:trunc(3/2*ih/vsub)*vsub" scale='min(500\, iw*3/2):-1' @end example +@section select +Select frames to pass in output. + +It accepts in input an expression, which is evaluated for each input +frame. If the expression is evaluated to a non-zero value, the frame +is selected and passed to the output, otherwise it is discarded. + +The expression can contain the following constants: + +@table @option +@item PI +Greek PI + +@item PHI +golden ratio + +@item E +Euler number + +@item n +the sequential number of the filtered frame, starting from 0 + +@item selected_n +the sequential number of the selected frame, starting from 0 + +@item prev_selected_n +the sequential number of the last selected frame, NAN if undefined + +@item TB +timebase of the input timestamps + +@item pts +the PTS (Presentation TimeStamp) of the filtered video frame, +expressed in @var{TB} units, NAN if undefined + +@item t +the PTS (Presentation TimeStamp) of the filtered video frame, +expressed in seconds, NAN if undefined + +@item prev_pts +the PTS of the previously filtered video frame, NAN if undefined + +@item prev_selected_pts +the PTS of the last previously filtered video frame, NAN if undefined + +@item prev_selected_t +the PTS of the last previously selected video frame, NAN if undefined + +@item start_pts +the PTS of the first video frame in the video, NAN if undefined + +@item start_t +the time of the first video frame in the video, NAN if undefined + +@item pict_type +the type of the filtered frame, can assume one of the following +values: +@table @option +@item I +@item P +@item B +@item S +@item SI +@item SP +@item BI +@end table + +@item interlace_type +the frame interlace type, can assume one of the following values: +@table @option +@item PROGRESSIVE +the frame is progressive (not interlaced) +@item TOPFIRST +the frame is top-field-first +@item BOTTOMFIRST +the frame is bottom-field-first +@end table + +@item key +1 if the filtered frame is a key-frame, 0 otherwise + +@item pos +the position in the file of the filtered frame, -1 if the information +is not available (e.g. for synthetic video) +@end table + +The default value of the select expression is "1". + +Some examples follow: + +@example +# select all frames in input +select + +# the above is the same as: +select=1 + +# skip all frames: +select=0 + +# select only I-frames +select='eq(pict_type\,I)' + +# select one frame every 100 +select='not(mod(n\,100))' + +# select only frames contained in the 10-20 time interval +select='gte(t\,10)*lte(t\,20)' + +# select only I frames contained in the 10-20 time interval +select='gte(t\,10)*lte(t\,20)*eq(pict_type\,I)' + +# select frames with a minimum distance of 10 seconds +select='isnan(prev_selected_t)+gte(t-prev_selected_t\,10)' +@end example + @anchor{setdar} @section setdar diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 5844dd0581..4bd06e9f2f 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -40,6 +40,7 @@ OBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o OBJS-$(CONFIG_PIXDESCTEST_FILTER) += vf_pixdesctest.o OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o +OBJS-$(CONFIG_SELECT_FILTER) += vf_select.o OBJS-$(CONFIG_SETDAR_FILTER) += vf_aspect.o OBJS-$(CONFIG_SETPTS_FILTER) += vf_setpts.o OBJS-$(CONFIG_SETSAR_FILTER) += vf_aspect.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 11e9d7d052..dbbe441859 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -61,6 +61,7 @@ void avfilter_register_all(void) REGISTER_FILTER (PAD, pad, vf); REGISTER_FILTER (PIXDESCTEST, pixdesctest, vf); REGISTER_FILTER (SCALE, scale, vf); + REGISTER_FILTER (SELECT, select, vf); REGISTER_FILTER (SETDAR, setdar, vf); REGISTER_FILTER (SETPTS, setpts, vf); REGISTER_FILTER (SETSAR, setsar, vf); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 3fcc8471e1..b9af1c4773 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 6 +#define LIBAVFILTER_VERSION_MINOR 7 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_select.c b/libavfilter/vf_select.c new file mode 100644 index 0000000000..0ec443aec5 --- /dev/null +++ b/libavfilter/vf_select.c @@ -0,0 +1,353 @@ +/* + * 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 + * filter for selecting which frame passes in the filterchain + */ + +#include "libavutil/eval.h" +#include "libavutil/fifo.h" +#include "libavutil/mathematics.h" +#include "avfilter.h" + +static const char *var_names[] = { + "E", ///< Euler number + "PHI", ///< golden ratio + "PI", ///< greek pi + + "TB", ///< timebase + + "pts", ///< original pts in the file of the frame + "start_pts", ///< first PTS in the stream, expressed in TB units + "prev_pts", ///< previous frame PTS + "prev_selected_pts", ///< previous selected frame PTS + + "t", ///< first PTS in seconds + "start_t", ///< first PTS in the stream, expressed in seconds + "prev_t", ///< previous frame time + "prev_selected_t", ///< previously selected time + + "pict_type", ///< the type of picture in the movie + "I", + "P", + "B", + "S", + "SI", + "SP", + "BI", + + "interlace_type", ///< the frame interlace type + "PROGRESSIVE", + "TOPFIRST", + "BOTTOMFIRST", + + "n", ///< frame number (starting from zero) + "selected_n", ///< selected frame number (starting from zero) + "prev_selected_n", ///< number of the last selected frame + + "key", ///< tell if the frame is a key frame + "pos", ///< original position in the file of the frame + + NULL +}; + +enum var_name { + VAR_E, + VAR_PHI, + VAR_PI, + + VAR_TB, + + VAR_PTS, + VAR_START_PTS, + VAR_PREV_PTS, + VAR_PREV_SELECTED_PTS, + + VAR_T, + VAR_START_T, + VAR_PREV_T, + VAR_PREV_SELECTED_T, + + VAR_PICT_TYPE, + VAR_PICT_TYPE_I, + VAR_PICT_TYPE_P, + VAR_PICT_TYPE_B, + VAR_PICT_TYPE_S, + VAR_PICT_TYPE_SI, + VAR_PICT_TYPE_SP, + VAR_PICT_TYPE_BI, + + VAR_INTERLACE_TYPE, + VAR_INTERLACE_TYPE_P, + VAR_INTERLACE_TYPE_T, + VAR_INTERLACE_TYPE_B, + + VAR_N, + VAR_SELECTED_N, + VAR_PREV_SELECTED_N, + + VAR_KEY, + VAR_POS, + + VAR_VARS_NB +}; + +#define FIFO_SIZE 8 + +typedef struct { + AVExpr *expr; + double var_values[VAR_VARS_NB]; + double select; + int cache_frames; + AVFifoBuffer *pending_frames; ///< FIFO buffer of video frames +} SelectContext; + +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + SelectContext *select = ctx->priv; + int ret; + + if ((ret = av_expr_parse(&select->expr, args ? args : "1", + var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", args); + return ret; + } + + select->pending_frames = av_fifo_alloc(FIFO_SIZE*sizeof(AVFilterBufferRef*)); + if (!select->pending_frames) { + av_log(ctx, AV_LOG_ERROR, "Failed to allocate pending frames buffer.\n"); + return AVERROR(ENOMEM); + } + return 0; +} + +#define INTERLACE_TYPE_P 0 +#define INTERLACE_TYPE_T 1 +#define INTERLACE_TYPE_B 2 + +static int config_input(AVFilterLink *inlink) +{ + SelectContext *select = inlink->dst->priv; + + select->var_values[VAR_E] = M_E; + select->var_values[VAR_PHI] = M_PHI; + select->var_values[VAR_PI] = M_PI; + + select->var_values[VAR_N] = 0.0; + select->var_values[VAR_SELECTED_N] = 0.0; + + select->var_values[VAR_TB] = av_q2d(inlink->time_base); + + select->var_values[VAR_PREV_PTS] = NAN; + select->var_values[VAR_PREV_SELECTED_PTS] = NAN; + select->var_values[VAR_PREV_SELECTED_T] = NAN; + select->var_values[VAR_START_PTS] = NAN; + select->var_values[VAR_START_T] = NAN; + + select->var_values[VAR_PICT_TYPE_I] = AV_PICTURE_TYPE_I; + select->var_values[VAR_PICT_TYPE_P] = AV_PICTURE_TYPE_P; + select->var_values[VAR_PICT_TYPE_B] = AV_PICTURE_TYPE_B; + select->var_values[VAR_PICT_TYPE_SI] = AV_PICTURE_TYPE_SI; + select->var_values[VAR_PICT_TYPE_SP] = AV_PICTURE_TYPE_SP; + + select->var_values[VAR_INTERLACE_TYPE_P] = INTERLACE_TYPE_P; + select->var_values[VAR_INTERLACE_TYPE_T] = INTERLACE_TYPE_T; + select->var_values[VAR_INTERLACE_TYPE_B] = INTERLACE_TYPE_B;; + + return 0; +} + +#define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d)) +#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)) + +static int select_frame(AVFilterContext *ctx, AVFilterBufferRef *picref) +{ + SelectContext *select = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + double res; + + if (isnan(select->var_values[VAR_START_PTS])) + select->var_values[VAR_START_PTS] = TS2D(picref->pts); + if (isnan(select->var_values[VAR_START_T])) + select->var_values[VAR_START_T] = TS2D(picref->pts) * av_q2d(inlink->time_base); + + select->var_values[VAR_PTS] = TS2D(picref->pts); + select->var_values[VAR_T ] = TS2D(picref->pts) * av_q2d(inlink->time_base); + select->var_values[VAR_POS] = picref->pos == -1 ? NAN : picref->pos; + select->var_values[VAR_PREV_PTS] = TS2D(picref ->pts); + + select->var_values[VAR_INTERLACE_TYPE] = + !picref->video->interlaced ? INTERLACE_TYPE_P : + picref->video->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B; + select->var_values[VAR_PICT_TYPE] = picref->video->pict_type; + + res = av_expr_eval(select->expr, select->var_values, NULL); + av_log(inlink->dst, AV_LOG_DEBUG, + "n:%d pts:%d t:%f pos:%d interlace_type:%c key:%d pict_type:%c " + "-> select:%f\n", + (int)select->var_values[VAR_N], + (int)select->var_values[VAR_PTS], + select->var_values[VAR_T], + (int)select->var_values[VAR_POS], + select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_P ? 'P' : + select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_T ? 'T' : + select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?', + (int)select->var_values[VAR_KEY], + av_get_picture_type_char(select->var_values[VAR_PICT_TYPE]), + res); + + select->var_values[VAR_N] += 1.0; + + if (res) { + select->var_values[VAR_PREV_SELECTED_N] = select->var_values[VAR_N]; + select->var_values[VAR_PREV_SELECTED_PTS] = select->var_values[VAR_PTS]; + select->var_values[VAR_PREV_SELECTED_T] = select->var_values[VAR_T]; + select->var_values[VAR_SELECTED_N] += 1.0; + } + return res; +} + +static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref) +{ + SelectContext *select = inlink->dst->priv; + + select->select = select_frame(inlink->dst, picref); + if (select->select) { + /* frame was requested through poll_frame */ + if (select->cache_frames) { + if (!av_fifo_space(select->pending_frames)) + av_log(inlink->dst, AV_LOG_ERROR, + "Buffering limit reached, cannot cache more frames\n"); + else + av_fifo_generic_write(select->pending_frames, &picref, + sizeof(picref), NULL); + return; + } + avfilter_start_frame(inlink->dst->outputs[0], avfilter_ref_buffer(picref, ~0)); + } +} + +static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) +{ + SelectContext *select = inlink->dst->priv; + + if (select->select && !select->cache_frames) + avfilter_draw_slice(inlink->dst->outputs[0], y, h, slice_dir); +} + +static void end_frame(AVFilterLink *inlink) +{ + SelectContext *select = inlink->dst->priv; + AVFilterBufferRef *picref = inlink->cur_buf; + + if (select->select) { + if (select->cache_frames) + return; + avfilter_end_frame(inlink->dst->outputs[0]); + } + avfilter_unref_buffer(picref); +} + +static int request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + SelectContext *select = ctx->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + select->select = 0; + + if (av_fifo_size(select->pending_frames)) { + AVFilterBufferRef *picref; + av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL); + avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0)); + avfilter_draw_slice(outlink, 0, outlink->h, 1); + avfilter_end_frame(outlink); + avfilter_unref_buffer(picref); + return 0; + } + + while (!select->select) { + int ret = avfilter_request_frame(inlink); + if (ret < 0) + return ret; + } + + return 0; +} + +static int poll_frame(AVFilterLink *outlink) +{ + SelectContext *select = outlink->src->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + int count, ret; + + if (!av_fifo_size(select->pending_frames)) { + if ((count = avfilter_poll_frame(inlink)) <= 0) + return count; + /* request frame from input, and apply select condition to it */ + select->cache_frames = 1; + while (count-- && av_fifo_space(select->pending_frames)) { + ret = avfilter_request_frame(inlink); + if (ret < 0) + break; + } + select->cache_frames = 0; + } + + return av_fifo_size(select->pending_frames)/sizeof(AVFilterBufferRef *); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + SelectContext *select = ctx->priv; + AVFilterBufferRef *picref; + + av_expr_free(select->expr); + select->expr = NULL; + + while (select->pending_frames && + av_fifo_generic_read(select->pending_frames, &picref, sizeof(picref), NULL) == sizeof(picref)) + avfilter_unref_buffer(picref); + av_fifo_free(select->pending_frames); + select->pending_frames = NULL; +} + +AVFilter avfilter_vf_select = { + .name = "select", + .description = NULL_IF_CONFIG_SMALL("Select frames to pass in output."), + .init = init, + .uninit = uninit, + + .priv_size = sizeof(SelectContext), + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer = avfilter_null_get_video_buffer, + .config_props = config_input, + .start_frame = start_frame, + .draw_slice = draw_slice, + .end_frame = end_frame }, + { .name = NULL }}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .poll_frame = poll_frame, + .request_frame = request_frame, }, + { .name = NULL}}, +};