ffmpeg-cvslog
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
January 2023
- 1 participants
- 273 discussions
30 Jan '23
ffmpeg | branch: master | Paul B Mahol <onemda(a)gmail.com> | Mon Jan 30 21:34:04 2023 +0100| [229f3259f80d824c7e7bb3bd5935ae4bc75d5374] | committer: Paul B Mahol
doc/general_contents: sync with available decoders and demuxers
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=229f3259f80d824c7…
---
doc/general_contents.texi | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 8399fcb6b7..87e180c979 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -534,6 +534,8 @@ library:
@item Metal Gear Solid: The Twin Snakes @tab @tab X
@item Megalux Frame @tab @tab X
@tab Used by Megalux Ultimate Paint
+@item MobiClip MODS @tab @tab X
+@item MobiClip MOFLEX @tab @tab X
@item Mobotix .mxg @tab @tab X
@item Monkey's Audio @tab @tab X
@item Motion Pixels MVI @tab @tab X
@@ -665,6 +667,7 @@ library:
@item SBG @tab @tab X
@item SDP @tab @tab X
@item SER @tab @tab X
+@item Digital Pictures SGA @tab @tab X
@item Sega FILM/CPK @tab X @tab X
@tab Used in many Sega Saturn console games.
@item Silicon Graphics Movie @tab @tab X
@@ -702,6 +705,7 @@ library:
@item Vivo @tab @tab X
@item VPK @tab @tab X
@tab Audio format used in Sony PS games.
+@item Marble WADY @tab @tab X
@item WAV @tab X @tab X
@item WavPack @tab X @tab X
@item WebM @tab X @tab X
@@ -714,6 +718,7 @@ library:
@tab Multimedia format used in Westwood Studios games.
@item Wideband Single-bit Data (WSD) @tab @tab X
@item WVE @tab @tab X
+@item Konami XMD @tab @tab X
@item XMV @tab @tab X
@tab Microsoft video container used in Xbox games.
@item XVAG @tab @tab X
@@ -763,6 +768,7 @@ following image formats are supported:
@item JPEG-LS @tab X @tab X
@item LJPEG @tab X @tab
@tab Lossless JPEG
+@item Media 100 @tab @tab X
@item MSP @tab @tab X
@tab Microsoft Paint image
@item PAM @tab X @tab X
@@ -1159,6 +1165,7 @@ following image formats are supported:
@item ADPCM IMA Electronic Arts SEAD @tab @tab X
@item ADPCM IMA Funcom @tab @tab X
@item ADPCM IMA High Voltage Software ALP @tab X @tab X
+@item ADPCM IMA Mobiclip MOFLEX @tab @tab X
@item ADPCM IMA QuickTime @tab X @tab X
@item ADPCM IMA Simon & Schuster Interactive @tab X @tab X
@item ADPCM IMA Ubisoft APM @tab X @tab X
@@ -1188,6 +1195,7 @@ following image formats are supported:
@item ADPCM Sound Blaster Pro 4-bit @tab @tab X
@item ADPCM VIMA @tab @tab X
@tab Used in LucasArts SMUSH animations.
+@item ADPCM Konami XMD @tab @tab X
@item ADPCM Westwood Studios IMA @tab X @tab X
@tab Used in Westwood Studios games like Command and Conquer.
@item ADPCM Yamaha @tab X @tab X
@@ -1225,9 +1233,12 @@ following image formats are supported:
@item DCA (DTS Coherent Acoustics) @tab X @tab X
@tab supported extensions: XCh, XXCH, X96, XBR, XLL, LBR (partially)
@item Dolby E @tab @tab X
+@item DPCM Cuberoot-Delta-Exact @tab @tab X
+ @tab Used in few games.
@item DPCM Gremlin @tab @tab X
@item DPCM id RoQ @tab X @tab X
@tab Used in Quake III, Jedi Knight 2 and other computer games.
+@item DPCM Marble WADY @tab @tab X
@item DPCM Interplay @tab @tab X
@tab Used in various Interplay computer games.
@item DPCM Squareroot-Delta-Exact @tab @tab X
@@ -1273,6 +1284,7 @@ following image formats are supported:
@item MP3 (MPEG audio layer 3) @tab E @tab IX
@tab encoding supported through external library LAME, ADU MP3 and MP3onMP4 also supported
@item MPEG-4 Audio Lossless Coding (ALS) @tab @tab X
+@item MobiClip FastAudio @tab @tab X
@item Musepack SV7 @tab @tab X
@item Musepack SV8 @tab @tab X
@item Nellymoser Asao @tab X @tab X
@@ -1307,6 +1319,7 @@ following image formats are supported:
@item PCM unsigned 24-bit little-endian @tab X @tab X
@item PCM unsigned 32-bit big-endian @tab X @tab X
@item PCM unsigned 32-bit little-endian @tab X @tab X
+@item PCM SGA @tab @tab X
@item QCELP / PureVoice @tab @tab X
@item QDesign Music Codec 1 @tab @tab X
@item QDesign Music Codec 2 @tab @tab X
1
0
ffmpeg | branch: master | Paul B Mahol <onemda(a)gmail.com> | Wed Jan 25 19:16:03 2023 +0100| [fa42575ffa30f226f6fda89cb192f315319f8575] | committer: Paul B Mahol
avformat: add XMD demuxer
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=fa42575ffa30f226f…
---
Changelog | 2 +-
libavformat/Makefile | 1 +
libavformat/allformats.c | 1 +
libavformat/version.h | 2 +-
libavformat/xmd.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 84 insertions(+), 2 deletions(-)
diff --git a/Changelog b/Changelog
index a0ba25257d..aa0dad13a1 100644
--- a/Changelog
+++ b/Changelog
@@ -34,7 +34,7 @@ version <next>:
- ssim360 video filter
- ffmpeg CLI new options: -enc_stats_pre[_fmt], -enc_stats_post[_fmt]
- hstack_vaapi, vstack_vaapi and xstack_vaapi filters
-- XMD ADPCM decoder
+- XMD ADPCM decoder and demuxer
version 5.1:
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 2d11bcd7a3..0bb9e8c19a 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -621,6 +621,7 @@ OBJS-$(CONFIG_WVE_DEMUXER) += wvedec.o pcm.o
OBJS-$(CONFIG_WV_MUXER) += wvenc.o wv.o apetag.o img2.o
OBJS-$(CONFIG_XA_DEMUXER) += xa.o
OBJS-$(CONFIG_XBIN_DEMUXER) += bintext.o sauce.o
+OBJS-$(CONFIG_XMD_DEMUXER) += xmd.o pcm.o
OBJS-$(CONFIG_XMV_DEMUXER) += xmv.o
OBJS-$(CONFIG_XVAG_DEMUXER) += xvag.o
OBJS-$(CONFIG_XWMA_DEMUXER) += xwma.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index bf8afe2078..2d31956fd5 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -501,6 +501,7 @@ extern const AVInputFormat ff_wv_demuxer;
extern const AVOutputFormat ff_wv_muxer;
extern const AVInputFormat ff_xa_demuxer;
extern const AVInputFormat ff_xbin_demuxer;
+extern const AVInputFormat ff_xmd_demuxer;
extern const AVInputFormat ff_xmv_demuxer;
extern const AVInputFormat ff_xvag_demuxer;
extern const AVInputFormat ff_xwma_demuxer;
diff --git a/libavformat/version.h b/libavformat/version.h
index 3970ec5cdd..789859fbe2 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,7 +31,7 @@
#include "version_major.h"
-#define LIBAVFORMAT_VERSION_MINOR 36
+#define LIBAVFORMAT_VERSION_MINOR 37
#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
diff --git a/libavformat/xmd.c b/libavformat/xmd.c
new file mode 100644
index 0000000000..f9c8138d72
--- /dev/null
+++ b/libavformat/xmd.c
@@ -0,0 +1,80 @@
+/*
+ * XMD demuxer
+ * Copyright (c) 2023 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/channel_layout.h"
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "demux.h"
+#include "internal.h"
+#include "pcm.h"
+
+static int xmd_probe(const AVProbeData *p)
+{
+ if ((AV_RL32(p->buf) & 0xFFFFFF) != MKTAG('x','m','d',0))
+ return 0;
+ if (p->buf[3] == 0 || p->buf[3] > 2 ||
+ AV_RL16(p->buf+4) == 0 ||
+ AV_RL32(p->buf+6) == 0)
+ return 0;
+
+ return AVPROBE_SCORE_MAX / 3;
+}
+
+static int xmd_read_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ AVCodecParameters *par;
+ int channels;
+ AVStream *st;
+
+ avio_skip(pb, 3);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ par = st->codecpar;
+ par->codec_type = AVMEDIA_TYPE_AUDIO;
+ par->codec_id = AV_CODEC_ID_ADPCM_XMD;
+ channels = avio_r8(pb);
+ if (channels == 0)
+ return AVERROR_INVALIDDATA;
+ av_channel_layout_default(&par->ch_layout, channels);
+ par->sample_rate = avio_rl16(pb);
+ if (par->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ par->block_align = 21 * channels;
+ st->duration = (avio_rl32(pb) / par->block_align) * 32LL;
+ avpriv_set_pts_info(st, 64, 1, par->sample_rate);
+ avio_skip(pb, 7);
+
+ return 0;
+}
+
+const AVInputFormat ff_xmd_demuxer = {
+ .name = "xmd",
+ .long_name = NULL_IF_CONFIG_SMALL("Konami XMD"),
+ .read_probe = xmd_probe,
+ .read_header = xmd_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .extensions = "xmd",
+};
1
0
ffmpeg | branch: master | Paul B Mahol <onemda(a)gmail.com> | Wed Jan 25 19:31:44 2023 +0100| [280e6e93fc96afbbb73f1987eb3d603f8949ea30] | committer: Paul B Mahol
avcodec: add ADPCM XMD decoder
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=280e6e93fc96afbbb…
---
Changelog | 1 +
libavcodec/Makefile | 1 +
libavcodec/adpcm.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
libavcodec/allcodecs.c | 1 +
libavcodec/codec_desc.c | 7 +++++++
libavcodec/codec_id.h | 1 +
libavcodec/utils.c | 3 +++
libavcodec/version.h | 2 +-
8 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/Changelog b/Changelog
index a0f1ad7211..a0ba25257d 100644
--- a/Changelog
+++ b/Changelog
@@ -34,6 +34,7 @@ version <next>:
- ssim360 video filter
- ffmpeg CLI new options: -enc_stats_pre[_fmt], -enc_stats_post[_fmt]
- hstack_vaapi, vstack_vaapi and xstack_vaapi filters
+- XMD ADPCM decoder
version 5.1:
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index f0ffd0b961..629933d85c 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -959,6 +959,7 @@ OBJS-$(CONFIG_ADPCM_THP_DECODER) += adpcm.o adpcm_data.o
OBJS-$(CONFIG_ADPCM_THP_LE_DECODER) += adpcm.o adpcm_data.o
OBJS-$(CONFIG_ADPCM_VIMA_DECODER) += vima.o adpcm_data.o
OBJS-$(CONFIG_ADPCM_XA_DECODER) += adpcm.o adpcm_data.o
+OBJS-$(CONFIG_ADPCM_XMD_DECODER) += adpcm.o adpcm_data.o
OBJS-$(CONFIG_ADPCM_YAMAHA_DECODER) += adpcm.o adpcm_data.o
OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER) += adpcmenc.o adpcm_data.o
OBJS-$(CONFIG_ADPCM_ZORK_DECODER) += adpcm.o adpcm_data.o
diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c
index 841538b138..451696932d 100644
--- a/libavcodec/adpcm.c
+++ b/libavcodec/adpcm.c
@@ -324,6 +324,7 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx)
case AV_CODEC_ID_ADPCM_IMA_WAV:
case AV_CODEC_ID_ADPCM_4XM:
case AV_CODEC_ID_ADPCM_XA:
+ case AV_CODEC_ID_ADPCM_XMD:
case AV_CODEC_ID_ADPCM_EA_R1:
case AV_CODEC_ID_ADPCM_EA_R2:
case AV_CODEC_ID_ADPCM_EA_R3:
@@ -1043,6 +1044,9 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb,
case AV_CODEC_ID_ADPCM_XA:
nb_samples = (buf_size / 128) * 224 / ch;
break;
+ case AV_CODEC_ID_ADPCM_XMD:
+ nb_samples = buf_size / (21 * ch) * 32;
+ break;
case AV_CODEC_ID_ADPCM_DTK:
case AV_CODEC_ID_ADPCM_PSX:
nb_samples = buf_size / (16 * ch) * 28;
@@ -1553,6 +1557,45 @@ static int adpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
}
bytestream2_seek(&gb, 0, SEEK_END);
) /* End of CASE */
+ CASE(ADPCM_XMD,
+ int bytes_remaining, block = 0;
+ while (bytestream2_get_bytes_left(&gb) >= 21 * channels) {
+ for (int channel = 0; channel < channels; channel++) {
+ int16_t *out = samples_p[channel] + block * 32;
+ int16_t history[2];
+ uint16_t scale;
+
+ history[1] = sign_extend(bytestream2_get_le16(&gb), 16);
+ history[0] = sign_extend(bytestream2_get_le16(&gb), 16);
+ scale = bytestream2_get_le16(&gb);
+
+ out[0] = history[1];
+ out[1] = history[0];
+
+ for (int n = 0; n < 15; n++) {
+ unsigned byte = bytestream2_get_byte(&gb);
+ int32_t nibble[2];
+
+ nibble[0] = sign_extend(byte & 15, 4);
+ nibble[1] = sign_extend(byte >> 4, 4);
+
+ out[2+n*2] = (nibble[0]*(scale<<14) + (history[0]*29336) - (history[1]*13136)) >> 14;
+ history[1] = history[0];
+ history[0] = out[2+n*2];
+
+ out[2+n*2+1] = (nibble[1]*(scale<<14) + (history[0]*29336) - (history[1]*13136)) >> 14;
+ history[1] = history[0];
+ history[0] = out[2+n*2+1];
+ }
+ }
+
+ block++;
+ }
+ bytes_remaining = bytestream2_get_bytes_left(&gb);
+ if (bytes_remaining > 0) {
+ bytestream2_skip(&gb, bytes_remaining);
+ }
+ ) /* End of CASE */
CASE(ADPCM_XA,
int16_t *out0 = samples_p[0];
int16_t *out1 = samples_p[1];
@@ -2350,5 +2393,6 @@ ADPCM_DECODER(ADPCM_SWF, sample_fmts_s16, adpcm_swf, "ADPCM Sho
ADPCM_DECODER(ADPCM_THP_LE, sample_fmts_s16p, adpcm_thp_le, "ADPCM Nintendo THP (little-endian)")
ADPCM_DECODER(ADPCM_THP, sample_fmts_s16p, adpcm_thp, "ADPCM Nintendo THP")
ADPCM_DECODER(ADPCM_XA, sample_fmts_s16p, adpcm_xa, "ADPCM CDROM XA")
+ADPCM_DECODER(ADPCM_XMD, sample_fmts_s16p, adpcm_xmd, "ADPCM Konami XMD")
ADPCM_DECODER(ADPCM_YAMAHA, sample_fmts_s16, adpcm_yamaha, "ADPCM Yamaha")
ADPCM_DECODER(ADPCM_ZORK, sample_fmts_s16, adpcm_zork, "ADPCM Zork")
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index f30047e17a..0deaea65b3 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -695,6 +695,7 @@ extern const FFCodec ff_adpcm_thp_decoder;
extern const FFCodec ff_adpcm_thp_le_decoder;
extern const FFCodec ff_adpcm_vima_decoder;
extern const FFCodec ff_adpcm_xa_decoder;
+extern const FFCodec ff_adpcm_xmd_decoder;
extern const FFCodec ff_adpcm_yamaha_encoder;
extern const FFCodec ff_adpcm_yamaha_decoder;
extern const FFCodec ff_adpcm_zork_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 50f9794e10..fbb6604f76 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -2536,6 +2536,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
.long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Acorn Replay"),
.props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
},
+ {
+ .id = AV_CODEC_ID_ADPCM_XMD,
+ .type = AVMEDIA_TYPE_AUDIO,
+ .name = "adpcm_xmd",
+ .long_name = NULL_IF_CONFIG_SMALL("ADPCM Konami XMD"),
+ .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
+ },
/* AMR */
{
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index cdf7eb79c3..3e84098f35 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -413,6 +413,7 @@ enum AVCodecID {
AV_CODEC_ID_ADPCM_IMA_CUNNING,
AV_CODEC_ID_ADPCM_IMA_MOFLEX,
AV_CODEC_ID_ADPCM_IMA_ACORN,
+ AV_CODEC_ID_ADPCM_XMD,
/* AMR */
AV_CODEC_ID_AMR_NB = 0x12000,
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 94f7ae6877..18a433b1af 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -769,6 +769,9 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba,
case AV_CODEC_ID_ADPCM_MTAF:
tmp = blocks * (ba - 16LL) * 2 / ch;
break;
+ case AV_CODEC_ID_ADPCM_XMD:
+ tmp = blocks * 32;
+ break;
}
if (tmp) {
if (tmp != (int)tmp)
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 752adc81f8..2ed4ef5547 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "version_major.h"
-#define LIBAVCODEC_VERSION_MINOR 59
+#define LIBAVCODEC_VERSION_MINOR 60
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
1
0
ffmpeg | branch: master | Niklas Haas <git(a)haasn.dev> | Mon Jan 30 20:34:55 2023 +0100| [9bfdd8524e4d3657be4057798bd4f015322827b2] | committer: Niklas Haas
avfilter/vf_libplacebo: suppress cast warning
This warning was introduced when ee65039 removed the cast.
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=9bfdd8524e4d3657b…
---
libavfilter/vf_libplacebo.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index d3c62d12a4..12abca4cc7 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -262,8 +262,7 @@ static int init_vulkan(AVFilterContext *avctx)
return AVERROR(EINVAL);
}
- avhwctx = avctx->hw_device_ctx->data;
-
+ avhwctx = (AVHWDeviceContext *) avctx->hw_device_ctx->data;
if (avhwctx->type != AV_HWDEVICE_TYPE_VULKAN) {
av_log(s, AV_LOG_ERROR, "Expected vulkan hwdevice for vf_libplacebo, got %s.\n",
av_hwdevice_get_type_name(avhwctx->type));
1
0
ffmpeg | branch: master | Paul B Mahol <onemda(a)gmail.com> | Mon Jan 30 17:01:26 2023 +0100| [5508e04c7b710616e33407ac2b3f8d616cb745f0] | committer: Paul B Mahol
avfilter/vf_colorlevels: add support for gbrpf32 format
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=5508e04c7b710616e…
---
libavfilter/vf_colorlevels.c | 118 +++++++++++++++++++++++++++++++++----------
1 file changed, 90 insertions(+), 28 deletions(-)
diff --git a/libavfilter/vf_colorlevels.c b/libavfilter/vf_colorlevels.c
index 76adfbd8c8..44ae6cffbf 100644
--- a/libavfilter/vf_colorlevels.c
+++ b/libavfilter/vf_colorlevels.c
@@ -97,11 +97,13 @@ typedef struct ThreadData {
int h;
+ float fimin[4];
+ float fomin[4];
int imin[4];
int omin[4];
} ThreadData;
-#define DO_COMMON(type, clip, preserve, planar) \
+#define DO_COMMON(type, ptype, clip, preserve, planar) \
const ThreadData *td = arg; \
const int linesize = s->linesize; \
const int step = s->step; \
@@ -118,14 +120,14 @@ typedef struct ThreadData {
type *dst_g = (type *)(td->dstrow[G]) + src_linesize * slice_start; \
type *dst_b = (type *)(td->dstrow[B]) + src_linesize * slice_start; \
type *dst_a = (type *)(td->dstrow[A]) + src_linesize * slice_start; \
- const int imin_r = td->imin[R]; \
- const int imin_g = td->imin[G]; \
- const int imin_b = td->imin[B]; \
- const int imin_a = td->imin[A]; \
- const int omin_r = td->omin[R]; \
- const int omin_g = td->omin[G]; \
- const int omin_b = td->omin[B]; \
- const int omin_a = td->omin[A]; \
+ const ptype imin_r = s->depth == 32 ? td->fimin[R] : td->imin[R]; \
+ const ptype imin_g = s->depth == 32 ? td->fimin[G] : td->imin[G]; \
+ const ptype imin_b = s->depth == 32 ? td->fimin[B] : td->imin[B]; \
+ const ptype imin_a = s->depth == 32 ? td->fimin[A] : td->imin[A]; \
+ const ptype omin_r = s->depth == 32 ? td->fomin[R] : td->omin[R]; \
+ const ptype omin_g = s->depth == 32 ? td->fomin[G] : td->omin[G]; \
+ const ptype omin_b = s->depth == 32 ? td->fomin[B] : td->omin[B]; \
+ const ptype omin_a = s->depth == 32 ? td->fomin[A] : td->omin[A]; \
const float coeff_r = td->coeff[R]; \
const float coeff_g = td->coeff[G]; \
const float coeff_b = td->coeff[B]; \
@@ -133,12 +135,12 @@ typedef struct ThreadData {
\
for (int y = slice_start; y < slice_end; y++) { \
for (int x = 0; x < linesize; x += step) { \
- int ir, ig, ib, or, og, ob; \
+ ptype ir, ig, ib, or, og, ob; \
ir = src_r[x]; \
ig = src_g[x]; \
ib = src_b[x]; \
if (preserve) { \
- float ratio, icolor, ocolor, max = s->max; \
+ float ratio, icolor, ocolor, max = s->depth==32 ? 1.f : s->max; \
\
or = (ir - imin_r) * coeff_r + omin_r; \
og = (ig - imin_g) * coeff_g + omin_g; \
@@ -180,39 +182,40 @@ typedef struct ThreadData {
#define CLIP8(x, depth) av_clip_uint8(x)
#define CLIP16(x, depth) av_clip_uint16(x)
+#define NOCLIP(x, depth) (x)
static int colorlevels_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
ColorLevelsContext *s = ctx->priv;
- DO_COMMON(uint8_t, CLIP8, 0, 0)
+ DO_COMMON(uint8_t, int, CLIP8, 0, 0)
return 0;
}
static int colorlevels_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
ColorLevelsContext *s = ctx->priv;\
- DO_COMMON(uint16_t, CLIP16, 0, 0)
+ DO_COMMON(uint16_t, int, CLIP16, 0, 0)
return 0;
}
static int colorlevels_preserve_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
ColorLevelsContext *s = ctx->priv;
- DO_COMMON(uint8_t, CLIP8, 1, 0)
+ DO_COMMON(uint8_t, int, CLIP8, 1, 0)
return 0;
}
static int colorlevels_preserve_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
ColorLevelsContext *s = ctx->priv;
- DO_COMMON(uint16_t, CLIP16, 1, 0)
+ DO_COMMON(uint16_t, int, CLIP16, 1, 0)
return 0;
}
static int colorlevels_slice_8_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
ColorLevelsContext *s = ctx->priv;
- DO_COMMON(uint8_t, CLIP8, 0, 1)
+ DO_COMMON(uint8_t, int, CLIP8, 0, 1)
return 0;
}
@@ -220,7 +223,7 @@ static int colorlevels_slice_9_planar(AVFilterContext *ctx, void *arg, int jobnr
{
ColorLevelsContext *s = ctx->priv;
const int depth = 9;
- DO_COMMON(uint16_t, av_clip_uintp2, 0, 1)
+ DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1)
return 0;
}
@@ -228,7 +231,7 @@ static int colorlevels_slice_10_planar(AVFilterContext *ctx, void *arg, int jobn
{
ColorLevelsContext *s = ctx->priv;
const int depth = 10;
- DO_COMMON(uint16_t, av_clip_uintp2, 0, 1)
+ DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1)
return 0;
}
@@ -236,7 +239,7 @@ static int colorlevels_slice_12_planar(AVFilterContext *ctx, void *arg, int jobn
{
ColorLevelsContext *s = ctx->priv;
const int depth = 12;
- DO_COMMON(uint16_t, av_clip_uintp2, 0, 1)
+ DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1)
return 0;
}
@@ -244,21 +247,28 @@ static int colorlevels_slice_14_planar(AVFilterContext *ctx, void *arg, int jobn
{
ColorLevelsContext *s = ctx->priv;
const int depth = 14;
- DO_COMMON(uint16_t, av_clip_uintp2, 0, 1)
+ DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1)
return 0;
}
static int colorlevels_slice_16_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
ColorLevelsContext *s = ctx->priv;
- DO_COMMON(uint16_t, CLIP16, 0, 1)
+ DO_COMMON(uint16_t, int, CLIP16, 0, 1)
+ return 0;
+}
+
+static int colorlevels_slice_32_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+ ColorLevelsContext *s = ctx->priv;
+ DO_COMMON(float, float, NOCLIP, 0, 1)
return 0;
}
static int colorlevels_preserve_slice_8_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
ColorLevelsContext *s = ctx->priv;
- DO_COMMON(uint8_t, CLIP8, 1, 1)
+ DO_COMMON(uint8_t, int, CLIP8, 1, 1)
return 0;
}
@@ -266,7 +276,7 @@ static int colorlevels_preserve_slice_9_planar(AVFilterContext *ctx, void *arg,
{
ColorLevelsContext *s = ctx->priv;
const int depth = 9;
- DO_COMMON(uint16_t, av_clip_uintp2, 1, 1)
+ DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1)
return 0;
}
@@ -274,7 +284,7 @@ static int colorlevels_preserve_slice_10_planar(AVFilterContext *ctx, void *arg,
{
ColorLevelsContext *s = ctx->priv;
const int depth = 10;
- DO_COMMON(uint16_t, av_clip_uintp2, 1, 1)
+ DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1)
return 0;
}
@@ -282,7 +292,7 @@ static int colorlevels_preserve_slice_12_planar(AVFilterContext *ctx, void *arg,
{
ColorLevelsContext *s = ctx->priv;
const int depth = 12;
- DO_COMMON(uint16_t, av_clip_uintp2, 1, 1)
+ DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1)
return 0;
}
@@ -290,14 +300,21 @@ static int colorlevels_preserve_slice_14_planar(AVFilterContext *ctx, void *arg,
{
ColorLevelsContext *s = ctx->priv;
const int depth = 14;
- DO_COMMON(uint16_t, av_clip_uintp2, 1, 1)
+ DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1)
return 0;
}
static int colorlevels_preserve_slice_16_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
ColorLevelsContext *s = ctx->priv;
- DO_COMMON(uint16_t, CLIP16, 1, 1)
+ DO_COMMON(uint16_t, int, CLIP16, 1, 1)
+ return 0;
+}
+
+static int colorlevels_preserve_slice_32_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+ ColorLevelsContext *s = ctx->priv;
+ DO_COMMON(float, float, NOCLIP, 1, 1)
return 0;
}
@@ -349,6 +366,10 @@ static int config_input(AVFilterLink *inlink)
s->colorlevels_slice[0] = colorlevels_slice_16_planar;
s->colorlevels_slice[1] = colorlevels_preserve_slice_16_planar;
break;
+ case 32:
+ s->colorlevels_slice[0] = colorlevels_slice_32_planar;
+ s->colorlevels_slice[1] = colorlevels_preserve_slice_32_planar;
+ break;
}
}
@@ -479,6 +500,46 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
td.omin[i] = omin;
}
break;
+ case 4:
+ for (int i = 0; i < s->nb_comp; i++) {
+ Range *r = &s->range[i];
+ const uint8_t offset = s->rgba_map[i];
+ const uint8_t *srcrow = in->data[0];
+ float imin = r->in_min;
+ float imax = r->in_max;
+ float omin = r->out_min;
+ float omax = r->out_max;
+ float coeff;
+
+ if (imin < 0.f) {
+ imin = 1.f;
+ for (int y = 0; y < inlink->h; y++) {
+ const float *src = (const float *)srcrow;
+
+ for (int x = 0; x < s->linesize; x += step)
+ imin = fminf(imin, src[x + offset]);
+ srcrow += in->linesize[0];
+ }
+ }
+ if (imax < 0.f) {
+ srcrow = in->data[0];
+ imax = 0.f;
+ for (int y = 0; y < inlink->h; y++) {
+ const float *src = (const float *)srcrow;
+
+ for (int x = 0; x < s->linesize; x += step)
+ imax = fmaxf(imax, src[x + offset]);
+ srcrow += in->linesize[0];
+ }
+ }
+
+ coeff = (omax - omin) / (double)(imax - imin);
+
+ td.coeff[i] = coeff;
+ td.fimin[i] = imin;
+ td.fomin[i] = omin;
+ }
+ break;
}
ff_filter_execute(ctx, s->colorlevels_slice[s->preserve_color > 0], &td, NULL,
@@ -524,7 +585,8 @@ const AVFilter ff_vf_colorlevels = {
AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
AV_PIX_FMT_GBRP14,
- AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16),
+ AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16,
+ AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32),
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
.process_command = ff_filter_process_command,
};
1
0
ffmpeg | branch: master | Haihao Xiang <haihao.xiang(a)intel.com> | Tue Jan 17 14:20:27 2023 +0800| [2d202985b79630cd5056c4e32f8f77f22bf1067c] | committer: Haihao Xiang
lavfi/vpp_qsv: factor out the code for filter definition
Signed-off-by: Haihao Xiang <haihao.xiang(a)intel.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=2d202985b79630cd5…
---
libavfilter/vf_vpp_qsv.c | 126 +++++++++++++++++++----------------------------
1 file changed, 51 insertions(+), 75 deletions(-)
diff --git a/libavfilter/vf_vpp_qsv.c b/libavfilter/vf_vpp_qsv.c
index 34732bb2c9..34e530e244 100644
--- a/libavfilter/vf_vpp_qsv.c
+++ b/libavfilter/vf_vpp_qsv.c
@@ -615,36 +615,31 @@ static const AVFilterPad vpp_outputs[] = {
},
};
-#if CONFIG_VPP_QSV_FILTER
-
-static int query_formats(AVFilterContext *ctx)
-{
- int ret;
- static const enum AVPixelFormat in_pix_fmts[] = {
- AV_PIX_FMT_YUV420P,
- AV_PIX_FMT_NV12,
- AV_PIX_FMT_YUYV422,
- AV_PIX_FMT_RGB32,
- AV_PIX_FMT_P010,
- AV_PIX_FMT_QSV,
- AV_PIX_FMT_NONE
- };
- static const enum AVPixelFormat out_pix_fmts[] = {
- AV_PIX_FMT_NV12,
- AV_PIX_FMT_P010,
- AV_PIX_FMT_QSV,
- AV_PIX_FMT_NONE
- };
+#define DEFINE_QSV_FILTER(x, sn, ln, fmts) \
+static const AVClass x##_class = { \
+ .class_name = #sn "_qsv", \
+ .item_name = av_default_item_name, \
+ .option = x##_options, \
+ .version = LIBAVUTIL_VERSION_INT, \
+}; \
+const AVFilter ff_vf_##sn##_qsv = { \
+ .name = #sn "_qsv", \
+ .description = NULL_IF_CONFIG_SMALL("Quick Sync Video " #ln), \
+ .preinit = x##_preinit, \
+ .init = vpp_init, \
+ .uninit = vpp_uninit, \
+ .priv_size = sizeof(VPPContext), \
+ .priv_class = &x##_class, \
+ FILTER_INPUTS(vpp_inputs), \
+ FILTER_OUTPUTS(vpp_outputs), \
+ fmts, \
+ .activate = activate, \
+ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \
+};
- ret = ff_formats_ref(ff_make_format_list(in_pix_fmts),
- &ctx->inputs[0]->outcfg.formats);
- if (ret < 0)
- return ret;
- return ff_formats_ref(ff_make_format_list(out_pix_fmts),
- &ctx->outputs[0]->incfg.formats);
-}
+#if CONFIG_VPP_QSV_FILTER
-static const AVOption options[] = {
+static const AVOption vpp_options[] = {
{ "deinterlace", "deinterlace mode: 0=off, 1=bob, 2=advanced", OFFSET(deinterlace), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MFX_DEINTERLACING_ADVANCED, .flags = FLAGS, "deinterlace" },
{ "bob", "Bob deinterlace mode.", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_BOB }, .flags = FLAGS, "deinterlace" },
{ "advanced", "Advanced deinterlace mode. ", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_ADVANCED }, .flags = FLAGS, "deinterlace" },
@@ -686,27 +681,34 @@ static const AVOption options[] = {
{ NULL }
};
-static const AVClass vpp_class = {
- .class_name = "vpp_qsv",
- .item_name = av_default_item_name,
- .option = options,
- .version = LIBAVUTIL_VERSION_INT,
-};
+static int vpp_query_formats(AVFilterContext *ctx)
+{
+ int ret;
+ static const enum AVPixelFormat in_pix_fmts[] = {
+ AV_PIX_FMT_YUV420P,
+ AV_PIX_FMT_NV12,
+ AV_PIX_FMT_YUYV422,
+ AV_PIX_FMT_RGB32,
+ AV_PIX_FMT_P010,
+ AV_PIX_FMT_QSV,
+ AV_PIX_FMT_NONE
+ };
+ static const enum AVPixelFormat out_pix_fmts[] = {
+ AV_PIX_FMT_NV12,
+ AV_PIX_FMT_P010,
+ AV_PIX_FMT_QSV,
+ AV_PIX_FMT_NONE
+ };
-const AVFilter ff_vf_vpp_qsv = {
- .name = "vpp_qsv",
- .description = NULL_IF_CONFIG_SMALL("Quick Sync Video VPP."),
- .priv_size = sizeof(VPPContext),
- .preinit = vpp_preinit,
- .init = vpp_init,
- .uninit = vpp_uninit,
- FILTER_INPUTS(vpp_inputs),
- FILTER_OUTPUTS(vpp_outputs),
- FILTER_QUERY_FUNC(query_formats),
- .activate = activate,
- .priv_class = &vpp_class,
- .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
-};
+ ret = ff_formats_ref(ff_make_format_list(in_pix_fmts),
+ &ctx->inputs[0]->outcfg.formats);
+ if (ret < 0)
+ return ret;
+ return ff_formats_ref(ff_make_format_list(out_pix_fmts),
+ &ctx->outputs[0]->incfg.formats);
+}
+
+DEFINE_QSV_FILTER(vpp, vpp, "VPP", FILTER_QUERY_FUNC(vpp_query_formats));
#endif
@@ -734,32 +736,6 @@ static av_cold int qsvscale_preinit(AVFilterContext *ctx)
return 0;
}
-static const AVClass qsvscale_class = {
- .class_name = "scale_qsv",
- .item_name = av_default_item_name,
- .option = qsvscale_options,
- .version = LIBAVUTIL_VERSION_INT,
-};
-
-const AVFilter ff_vf_scale_qsv = {
- .name = "scale_qsv",
- .description = NULL_IF_CONFIG_SMALL("Quick Sync Video scaling and format conversion"),
-
- .preinit = qsvscale_preinit,
- .init = vpp_init,
- .uninit = vpp_uninit,
-
- .priv_size = sizeof(VPPContext),
- .priv_class = &qsvscale_class,
-
- FILTER_INPUTS(vpp_inputs),
- FILTER_OUTPUTS(vpp_outputs),
-
- FILTER_SINGLE_PIXFMT(AV_PIX_FMT_QSV),
-
- .activate = activate,
-
- .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
-};
+DEFINE_QSV_FILTER(qsvscale, scale, "scaling and format conversion", FILTER_SINGLE_PIXFMT(AV_PIX_FMT_QSV));
#endif
1
0
ffmpeg | branch: master | Haihao Xiang <haihao.xiang(a)intel.com> | Tue Jan 17 14:20:26 2023 +0800| [9c09fb232b6863181ac1fe70647f9cbccb053d88] | committer: Haihao Xiang
lavfi/scale_qsv: re-use VPPContext for scale_qsv filter
QSVScaleContext and VPPContext have the same base context, and all
features in scale_qsv are implemented in vpp_qsv filter, so scale_qsv
can be taken as a special case of vpp_qsv filter, and we may use
VPPContext with a different option array, preinit callback and supported
pixel formats to implement scale_qsv then remove QSVScaleContext
Signed-off-by: Haihao Xiang <haihao.xiang(a)intel.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=9c09fb232b6863181…
---
libavfilter/Makefile | 2 +-
libavfilter/vf_scale_qsv.c | 290 ---------------------------------------------
libavfilter/vf_vpp_qsv.c | 186 +++++++++++++++++++----------
3 files changed, 124 insertions(+), 354 deletions(-)
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index a0059c69e6..b45dcd00fc 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -446,7 +446,7 @@ OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o scale_eval.o
OBJS-$(CONFIG_SCALE_CUDA_FILTER) += vf_scale_cuda.o scale_eval.o \
vf_scale_cuda.ptx.o cuda/load_helper.o
OBJS-$(CONFIG_SCALE_NPP_FILTER) += vf_scale_npp.o scale_eval.o
-OBJS-$(CONFIG_SCALE_QSV_FILTER) += vf_scale_qsv.o
+OBJS-$(CONFIG_SCALE_QSV_FILTER) += vf_vpp_qsv.o
OBJS-$(CONFIG_SCALE_VAAPI_FILTER) += vf_scale_vaapi.o scale_eval.o vaapi_vpp.o
OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vf_scale_vulkan.o vulkan.o vulkan_filter.o
OBJS-$(CONFIG_SCALE2REF_FILTER) += vf_scale.o scale_eval.o
diff --git a/libavfilter/vf_scale_qsv.c b/libavfilter/vf_scale_qsv.c
deleted file mode 100644
index 8eb8bc9ec1..0000000000
--- a/libavfilter/vf_scale_qsv.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * This file is part of FFmpeg.
- *
- * FFmpeg 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.
- *
- * FFmpeg 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 FFmpeg; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/**
- * @file
- * scale video filter - QSV
- */
-
-#include <mfxvideo.h>
-
-#include <stdio.h>
-#include <string.h>
-
-#include "libavutil/avstring.h"
-#include "libavutil/common.h"
-#include "libavutil/eval.h"
-#include "libavutil/hwcontext.h"
-#include "libavutil/hwcontext_qsv.h"
-#include "libavutil/internal.h"
-#include "libavutil/mathematics.h"
-#include "libavutil/opt.h"
-#include "libavutil/pixdesc.h"
-#include "libavutil/time.h"
-#include "libavfilter/qsvvpp.h"
-
-#include "avfilter.h"
-#include "formats.h"
-#include "internal.h"
-#include "video.h"
-
-static const char *const var_names[] = {
- "in_w", "iw",
- "in_h", "ih",
- "out_w", "ow",
- "out_h", "oh",
- "a", "dar",
- "sar",
- NULL
-};
-
-enum var_name {
- VAR_IN_W, VAR_IW,
- VAR_IN_H, VAR_IH,
- VAR_OUT_W, VAR_OW,
- VAR_OUT_H, VAR_OH,
- VAR_A, VAR_DAR,
- VAR_SAR,
- VARS_NB
-};
-
-#define MFX_IMPL_VIA_MASK(impl) (0x0f00 & (impl))
-
-typedef struct QSVScaleContext {
- QSVVPPContext qsv;
-
- mfxExtVPPScaling scale_conf;
- int mode;
-
- /**
- * New dimensions. Special values are:
- * 0 = original width/height
- * -1 = keep original aspect
- */
- int w, h;
-
- /**
- * Output sw format. AV_PIX_FMT_NONE for no conversion.
- */
- enum AVPixelFormat format;
-
- char *w_expr; ///< width expression string
- char *h_expr; ///< height expression string
- char *format_str;
-} QSVScaleContext;
-
-static av_cold int qsvscale_init(AVFilterContext *ctx)
-{
- QSVScaleContext *s = ctx->priv;
-
- if (!strcmp(s->format_str, "same")) {
- s->format = AV_PIX_FMT_NONE;
- } else {
- s->format = av_get_pix_fmt(s->format_str);
- if (s->format == AV_PIX_FMT_NONE) {
- av_log(ctx, AV_LOG_ERROR, "Unrecognized pixel format: %s\n", s->format_str);
- return AVERROR(EINVAL);
- }
- }
-
- return 0;
-}
-
-static av_cold void qsvscale_uninit(AVFilterContext *ctx)
-{
- ff_qsvvpp_close(ctx);
-}
-
-static int qsvscale_config_props(AVFilterLink *outlink)
-{
- AVFilterContext *ctx = outlink->src;
- AVFilterLink *inlink = outlink->src->inputs[0];
- QSVScaleContext *s = ctx->priv;
- QSVVPPParam param = { NULL };
- mfxExtBuffer *ext_buf[1];
- int64_t w, h;
- double var_values[VARS_NB], res;
- char *expr;
- int ret;
- enum AVPixelFormat in_format;
-
- var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w;
- var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h;
- var_values[VAR_OUT_W] = var_values[VAR_OW] = NAN;
- var_values[VAR_OUT_H] = var_values[VAR_OH] = NAN;
- var_values[VAR_A] = (double) inlink->w / inlink->h;
- var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ?
- (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
- var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR];
-
- /* evaluate width and height */
- av_expr_parse_and_eval(&res, (expr = s->w_expr),
- var_names, var_values,
- NULL, NULL, NULL, NULL, NULL, 0, ctx);
- s->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res;
- if ((ret = av_expr_parse_and_eval(&res, (expr = s->h_expr),
- var_names, var_values,
- NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
- goto fail;
- s->h = var_values[VAR_OUT_H] = var_values[VAR_OH] = res;
- /* evaluate again the width, as it may depend on the output height */
- if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr),
- var_names, var_values,
- NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
- goto fail;
- s->w = res;
-
- w = s->w;
- h = s->h;
-
- /* sanity check params */
- if (w < -1 || h < -1) {
- av_log(ctx, AV_LOG_ERROR, "Size values less than -1 are not acceptable.\n");
- return AVERROR(EINVAL);
- }
- if (w == -1 && h == -1)
- s->w = s->h = 0;
-
- if (!(w = s->w))
- w = inlink->w;
- if (!(h = s->h))
- h = inlink->h;
- if (w == -1)
- w = av_rescale(h, inlink->w, inlink->h);
- if (h == -1)
- h = av_rescale(w, inlink->h, inlink->w);
-
- if (w > INT_MAX || h > INT_MAX ||
- (h * inlink->w) > INT_MAX ||
- (w * inlink->h) > INT_MAX)
- av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
-
- outlink->w = w;
- outlink->h = h;
-
- if (inlink->format == AV_PIX_FMT_QSV) {
- if (!inlink->hw_frames_ctx || !inlink->hw_frames_ctx->data)
- return AVERROR(EINVAL);
- else
- in_format = ((AVHWFramesContext*)inlink->hw_frames_ctx->data)->sw_format;
- } else
- in_format = inlink->format;
-
- if (s->format == AV_PIX_FMT_NONE)
- s->format = in_format;
-
- outlink->frame_rate = inlink->frame_rate;
- outlink->time_base = av_inv_q(inlink->frame_rate);
- param.out_sw_format = s->format;
-
- param.ext_buf = ext_buf;
- memset(&s->scale_conf, 0, sizeof(mfxExtVPPScaling));
- s->scale_conf.Header.BufferId = MFX_EXTBUFF_VPP_SCALING;
- s->scale_conf.Header.BufferSz = sizeof(mfxExtVPPScaling);
- s->scale_conf.ScalingMode = s->mode;
- param.ext_buf[param.num_ext_buf++] = (mfxExtBuffer*)&s->scale_conf;
- av_log(ctx, AV_LOG_VERBOSE, "Scaling mode: %d\n", s->mode);
-
- ret = ff_qsvvpp_init(ctx, ¶m);
- if (ret < 0)
- return ret;
-
- av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d -> w:%d h:%d\n",
- inlink->w, inlink->h, outlink->w, outlink->h);
-
- if (inlink->sample_aspect_ratio.num)
- outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h*inlink->w,
- outlink->w*inlink->h},
- inlink->sample_aspect_ratio);
- else
- outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
-
- return 0;
-
-fail:
- av_log(ctx, AV_LOG_ERROR,
- "Error when evaluating the expression '%s'\n", expr);
- return ret;
-}
-
-static int qsvscale_filter_frame(AVFilterLink *link, AVFrame *in)
-{
- int ret = 0;
- AVFilterContext *ctx = link->dst;
- QSVVPPContext *qsv = ctx->priv;
-
- ret = ff_qsvvpp_filter_frame(qsv, link, in);
- av_frame_free(&in);
- return ret;
-}
-
-#define OFFSET(x) offsetof(QSVScaleContext, x)
-#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
-static const AVOption options[] = {
- { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str = "iw" }, .flags = FLAGS },
- { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str = "ih" }, .flags = FLAGS },
- { "format", "Output pixel format", OFFSET(format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS },
-
- { "mode", "set scaling mode", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT}, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, FLAGS, "mode"},
- { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, "mode"},
- { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, "mode"},
-
- { NULL },
-};
-
-static const AVClass qsvscale_class = {
- .class_name = "scale_qsv",
- .item_name = av_default_item_name,
- .option = options,
- .version = LIBAVUTIL_VERSION_INT,
-};
-
-static const AVFilterPad qsvscale_inputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .filter_frame = qsvscale_filter_frame,
- .get_buffer.video = ff_qsvvpp_get_video_buffer,
- },
-};
-
-static const AVFilterPad qsvscale_outputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .config_props = qsvscale_config_props,
- },
-};
-
-const AVFilter ff_vf_scale_qsv = {
- .name = "scale_qsv",
- .description = NULL_IF_CONFIG_SMALL("QuickSync video scaling and format conversion"),
-
- .init = qsvscale_init,
- .uninit = qsvscale_uninit,
-
- .priv_size = sizeof(QSVScaleContext),
- .priv_class = &qsvscale_class,
-
- FILTER_INPUTS(qsvscale_inputs),
- FILTER_OUTPUTS(qsvscale_outputs),
-
- FILTER_SINGLE_PIXFMT(AV_PIX_FMT_QSV),
-
- .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
-};
diff --git a/libavfilter/vf_vpp_qsv.c b/libavfilter/vf_vpp_qsv.c
index 4a1af7146a..34732bb2c9 100644
--- a/libavfilter/vf_vpp_qsv.c
+++ b/libavfilter/vf_vpp_qsv.c
@@ -23,6 +23,8 @@
#include <float.h>
+#include "config_components.h"
+
#include "libavutil/opt.h"
#include "libavutil/eval.h"
#include "libavutil/hwcontext.h"
@@ -101,48 +103,6 @@ typedef struct VPPContext{
int has_passthrough; /* apply pass through mode if possible */
} VPPContext;
-static const AVOption options[] = {
- { "deinterlace", "deinterlace mode: 0=off, 1=bob, 2=advanced", OFFSET(deinterlace), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MFX_DEINTERLACING_ADVANCED, .flags = FLAGS, "deinterlace" },
- { "bob", "Bob deinterlace mode.", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_BOB }, .flags = FLAGS, "deinterlace" },
- { "advanced", "Advanced deinterlace mode. ", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_ADVANCED }, .flags = FLAGS, "deinterlace" },
-
- { "denoise", "denoise level [0, 100]", OFFSET(denoise), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, .flags = FLAGS },
- { "detail", "enhancement level [0, 100]", OFFSET(detail), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, .flags = FLAGS },
- { "framerate", "output framerate", OFFSET(framerate), AV_OPT_TYPE_RATIONAL, { .dbl = 0.0 },0, DBL_MAX, .flags = FLAGS },
- { "procamp", "Enable ProcAmp", OFFSET(procamp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS},
- { "hue", "ProcAmp hue", OFFSET(hue), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, -180.0, 180.0, .flags = FLAGS},
- { "saturation", "ProcAmp saturation", OFFSET(saturation), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 10.0, .flags = FLAGS},
- { "contrast", "ProcAmp contrast", OFFSET(contrast), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 10.0, .flags = FLAGS},
- { "brightness", "ProcAmp brightness", OFFSET(brightness), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, -100.0, 100.0, .flags = FLAGS},
-
- { "transpose", "set transpose direction", OFFSET(transpose), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 6, FLAGS, "transpose"},
- { "cclock_hflip", "rotate counter-clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" },
- { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "transpose" },
- { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "transpose" },
- { "clock_hflip", "rotate clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" },
- { "reversal", "rotate by half-turn", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "transpose" },
- { "hflip", "flip horizontally", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "transpose" },
- { "vflip", "flip vertically", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "transpose" },
-
- { "cw", "set the width crop area expression", OFFSET(cw), AV_OPT_TYPE_STRING, { .str = "iw" }, 0, 0, FLAGS },
- { "ch", "set the height crop area expression", OFFSET(ch), AV_OPT_TYPE_STRING, { .str = "ih" }, 0, 0, FLAGS },
- { "cx", "set the x crop area expression", OFFSET(cx), AV_OPT_TYPE_STRING, { .str = "(in_w-out_w)/2" }, 0, 0, FLAGS },
- { "cy", "set the y crop area expression", OFFSET(cy), AV_OPT_TYPE_STRING, { .str = "(in_h-out_h)/2" }, 0, 0, FLAGS },
-
- { "w", "Output video width(0=input video width, -1=keep input video aspect)", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS },
- { "width", "Output video width(0=input video width, -1=keep input video aspect)", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS },
- { "h", "Output video height(0=input video height, -1=keep input video aspect)", OFFSET(oh), AV_OPT_TYPE_STRING, { .str="w*ch/cw" }, 0, 255, .flags = FLAGS },
- { "height", "Output video height(0=input video height, -1=keep input video aspect)", OFFSET(oh), AV_OPT_TYPE_STRING, { .str="w*ch/cw" }, 0, 255, .flags = FLAGS },
- { "format", "Output pixel format", OFFSET(output_format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS },
- { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = FLAGS },
- { "scale_mode", "scale & format conversion mode: 0=auto, 1=low power, 2=high quality", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT }, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, .flags = FLAGS, "scale mode" },
- { "auto", "auto mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_DEFAULT}, INT_MIN, INT_MAX, FLAGS, "scale mode"},
- { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, "scale mode"},
- { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, "scale mode"},
-
- { NULL }
-};
-
static const char *const var_names[] = {
"iw", "in_w",
"ih", "in_h",
@@ -633,6 +593,30 @@ eof:
return 0;
}
+static av_cold void vpp_uninit(AVFilterContext *ctx)
+{
+ ff_qsvvpp_close(ctx);
+}
+
+static const AVFilterPad vpp_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_input,
+ .get_buffer.video = ff_qsvvpp_get_video_buffer,
+ },
+};
+
+static const AVFilterPad vpp_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_output,
+ },
+};
+
+#if CONFIG_VPP_QSV_FILTER
+
static int query_formats(AVFilterContext *ctx)
{
int ret;
@@ -660,10 +644,47 @@ static int query_formats(AVFilterContext *ctx)
&ctx->outputs[0]->incfg.formats);
}
-static av_cold void vpp_uninit(AVFilterContext *ctx)
-{
- ff_qsvvpp_close(ctx);
-}
+static const AVOption options[] = {
+ { "deinterlace", "deinterlace mode: 0=off, 1=bob, 2=advanced", OFFSET(deinterlace), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MFX_DEINTERLACING_ADVANCED, .flags = FLAGS, "deinterlace" },
+ { "bob", "Bob deinterlace mode.", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_BOB }, .flags = FLAGS, "deinterlace" },
+ { "advanced", "Advanced deinterlace mode. ", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_ADVANCED }, .flags = FLAGS, "deinterlace" },
+
+ { "denoise", "denoise level [0, 100]", OFFSET(denoise), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, .flags = FLAGS },
+ { "detail", "enhancement level [0, 100]", OFFSET(detail), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, .flags = FLAGS },
+ { "framerate", "output framerate", OFFSET(framerate), AV_OPT_TYPE_RATIONAL, { .dbl = 0.0 },0, DBL_MAX, .flags = FLAGS },
+ { "procamp", "Enable ProcAmp", OFFSET(procamp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS},
+ { "hue", "ProcAmp hue", OFFSET(hue), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, -180.0, 180.0, .flags = FLAGS},
+ { "saturation", "ProcAmp saturation", OFFSET(saturation), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 10.0, .flags = FLAGS},
+ { "contrast", "ProcAmp contrast", OFFSET(contrast), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 10.0, .flags = FLAGS},
+ { "brightness", "ProcAmp brightness", OFFSET(brightness), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, -100.0, 100.0, .flags = FLAGS},
+
+ { "transpose", "set transpose direction", OFFSET(transpose), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 6, FLAGS, "transpose"},
+ { "cclock_hflip", "rotate counter-clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" },
+ { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "transpose" },
+ { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "transpose" },
+ { "clock_hflip", "rotate clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" },
+ { "reversal", "rotate by half-turn", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "transpose" },
+ { "hflip", "flip horizontally", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "transpose" },
+ { "vflip", "flip vertically", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "transpose" },
+
+ { "cw", "set the width crop area expression", OFFSET(cw), AV_OPT_TYPE_STRING, { .str = "iw" }, 0, 0, FLAGS },
+ { "ch", "set the height crop area expression", OFFSET(ch), AV_OPT_TYPE_STRING, { .str = "ih" }, 0, 0, FLAGS },
+ { "cx", "set the x crop area expression", OFFSET(cx), AV_OPT_TYPE_STRING, { .str = "(in_w-out_w)/2" }, 0, 0, FLAGS },
+ { "cy", "set the y crop area expression", OFFSET(cy), AV_OPT_TYPE_STRING, { .str = "(in_h-out_h)/2" }, 0, 0, FLAGS },
+
+ { "w", "Output video width(0=input video width, -1=keep input video aspect)", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS },
+ { "width", "Output video width(0=input video width, -1=keep input video aspect)", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS },
+ { "h", "Output video height(0=input video height, -1=keep input video aspect)", OFFSET(oh), AV_OPT_TYPE_STRING, { .str="w*ch/cw" }, 0, 255, .flags = FLAGS },
+ { "height", "Output video height(0=input video height, -1=keep input video aspect)", OFFSET(oh), AV_OPT_TYPE_STRING, { .str="w*ch/cw" }, 0, 255, .flags = FLAGS },
+ { "format", "Output pixel format", OFFSET(output_format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS },
+ { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = FLAGS },
+ { "scale_mode", "scale & format conversion mode: 0=auto, 1=low power, 2=high quality", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT }, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, .flags = FLAGS, "scale mode" },
+ { "auto", "auto mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_DEFAULT}, INT_MIN, INT_MAX, FLAGS, "scale mode"},
+ { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, "scale mode"},
+ { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, "scale mode"},
+
+ { NULL }
+};
static const AVClass vpp_class = {
.class_name = "vpp_qsv",
@@ -672,23 +693,6 @@ static const AVClass vpp_class = {
.version = LIBAVUTIL_VERSION_INT,
};
-static const AVFilterPad vpp_inputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .config_props = config_input,
- .get_buffer.video = ff_qsvvpp_get_video_buffer,
- },
-};
-
-static const AVFilterPad vpp_outputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .config_props = config_output,
- },
-};
-
const AVFilter ff_vf_vpp_qsv = {
.name = "vpp_qsv",
.description = NULL_IF_CONFIG_SMALL("Quick Sync Video VPP."),
@@ -703,3 +707,59 @@ const AVFilter ff_vf_vpp_qsv = {
.priv_class = &vpp_class,
.flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
};
+
+#endif
+
+#if CONFIG_SCALE_QSV_FILTER
+
+static const AVOption qsvscale_options[] = {
+ { "w", "Output video width(0=input video width, -1=keep input video aspect)", OFFSET(ow), AV_OPT_TYPE_STRING, { .str = "iw" }, .flags = FLAGS },
+ { "h", "Output video height(0=input video height, -1=keep input video aspect)", OFFSET(oh), AV_OPT_TYPE_STRING, { .str = "ih" }, .flags = FLAGS },
+ { "format", "Output pixel format", OFFSET(output_format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS },
+
+ { "mode", "set scaling mode", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT}, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, FLAGS, "mode"},
+ { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, "mode"},
+ { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, "mode"},
+
+ { NULL },
+};
+
+static av_cold int qsvscale_preinit(AVFilterContext *ctx)
+{
+ VPPContext *vpp = ctx->priv;
+
+ vpp_preinit(ctx);
+ vpp->has_passthrough = 0;
+
+ return 0;
+}
+
+static const AVClass qsvscale_class = {
+ .class_name = "scale_qsv",
+ .item_name = av_default_item_name,
+ .option = qsvscale_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const AVFilter ff_vf_scale_qsv = {
+ .name = "scale_qsv",
+ .description = NULL_IF_CONFIG_SMALL("Quick Sync Video scaling and format conversion"),
+
+ .preinit = qsvscale_preinit,
+ .init = vpp_init,
+ .uninit = vpp_uninit,
+
+ .priv_size = sizeof(VPPContext),
+ .priv_class = &qsvscale_class,
+
+ FILTER_INPUTS(vpp_inputs),
+ FILTER_OUTPUTS(vpp_outputs),
+
+ FILTER_SINGLE_PIXFMT(AV_PIX_FMT_QSV),
+
+ .activate = activate,
+
+ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+};
+
+#endif
1
0
ffmpeg | branch: master | Haihao Xiang <haihao.xiang(a)intel.com> | Tue Jan 17 14:20:25 2023 +0800| [d66312cf278f0b2db4d4410dd6a99bb4d1f30919] | committer: Haihao Xiang
lavfi/scale_qsv: simplify scale_qsv filter
Use QSVVPPContext as a base context of QSVScaleContext, hence we may
re-use functions defined for QSVVPPContext to manage MFX session for
scale_qsv filter.
In addition, system memory has been taken into account in
QSVVVPPContext, we may add support for non-QSV pixel formats in the
future.
Signed-off-by: Haihao Xiang <haihao.xiang(a)intel.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=d66312cf278f0b2db…
---
configure | 1 +
libavfilter/vf_scale_qsv.c | 441 ++++-----------------------------------------
2 files changed, 35 insertions(+), 407 deletions(-)
diff --git a/configure b/configure
index 8190e978a8..47790d10f5 100755
--- a/configure
+++ b/configure
@@ -3722,6 +3722,7 @@ sab_filter_deps="gpl swscale"
scale2ref_filter_deps="swscale"
scale_filter_deps="swscale"
scale_qsv_filter_deps="libmfx"
+scale_qsv_filter_select="qsvvpp"
scdet_filter_select="scene_sad"
select_filter_select="scene_sad"
sharpness_vaapi_filter_deps="vaapi"
diff --git a/libavfilter/vf_scale_qsv.c b/libavfilter/vf_scale_qsv.c
index a89a3ba6e6..8eb8bc9ec1 100644
--- a/libavfilter/vf_scale_qsv.c
+++ b/libavfilter/vf_scale_qsv.c
@@ -66,35 +66,11 @@ enum var_name {
#define MFX_IMPL_VIA_MASK(impl) (0x0f00 & (impl))
typedef struct QSVScaleContext {
- const AVClass *class;
-
- /* a clone of the main session, used internally for scaling */
- mfxSession session;
-
- mfxMemId *mem_ids_in;
- int nb_mem_ids_in;
-
- mfxMemId *mem_ids_out;
- int nb_mem_ids_out;
-
- mfxFrameSurface1 **surface_ptrs_in;
- int nb_surface_ptrs_in;
-
- mfxFrameSurface1 **surface_ptrs_out;
- int nb_surface_ptrs_out;
-
-#if QSV_HAVE_OPAQUE
- mfxExtOpaqueSurfaceAlloc opaque_alloc;
-#endif
+ QSVVPPContext qsv;
mfxExtVPPScaling scale_conf;
int mode;
- mfxExtBuffer *ext_buffers[2];
- int num_ext_buf;
-
- int shift_width, shift_height;
-
/**
* New dimensions. Special values are:
* 0 = original width/height
@@ -131,338 +107,21 @@ static av_cold int qsvscale_init(AVFilterContext *ctx)
static av_cold void qsvscale_uninit(AVFilterContext *ctx)
{
- QSVScaleContext *s = ctx->priv;
-
- if (s->session) {
- MFXClose(s->session);
- s->session = NULL;
- }
-
- av_freep(&s->mem_ids_in);
- av_freep(&s->mem_ids_out);
- s->nb_mem_ids_in = 0;
- s->nb_mem_ids_out = 0;
-
- av_freep(&s->surface_ptrs_in);
- av_freep(&s->surface_ptrs_out);
- s->nb_surface_ptrs_in = 0;
- s->nb_surface_ptrs_out = 0;
-}
-
-static int init_out_pool(AVFilterContext *ctx,
- int out_width, int out_height)
-{
- QSVScaleContext *s = ctx->priv;
- AVFilterLink *outlink = ctx->outputs[0];
-
- AVHWFramesContext *in_frames_ctx;
- AVHWFramesContext *out_frames_ctx;
- AVQSVFramesContext *in_frames_hwctx;
- AVQSVFramesContext *out_frames_hwctx;
- enum AVPixelFormat in_format;
- enum AVPixelFormat out_format;
- int i, ret;
-
- /* check that we have a hw context */
- if (!ctx->inputs[0]->hw_frames_ctx) {
- av_log(ctx, AV_LOG_ERROR, "No hw context provided on input\n");
- return AVERROR(EINVAL);
- }
- in_frames_ctx = (AVHWFramesContext*)ctx->inputs[0]->hw_frames_ctx->data;
- in_frames_hwctx = in_frames_ctx->hwctx;
-
- in_format = in_frames_ctx->sw_format;
- out_format = (s->format == AV_PIX_FMT_NONE) ? in_format : s->format;
-
- outlink->hw_frames_ctx = av_hwframe_ctx_alloc(in_frames_ctx->device_ref);
- if (!outlink->hw_frames_ctx)
- return AVERROR(ENOMEM);
- out_frames_ctx = (AVHWFramesContext*)outlink->hw_frames_ctx->data;
- out_frames_hwctx = out_frames_ctx->hwctx;
-
- out_frames_ctx->format = AV_PIX_FMT_QSV;
- out_frames_ctx->width = FFALIGN(out_width, 16);
- out_frames_ctx->height = FFALIGN(out_height, 16);
- out_frames_ctx->sw_format = out_format;
- out_frames_ctx->initial_pool_size = 4;
-
- out_frames_hwctx->frame_type = in_frames_hwctx->frame_type | MFX_MEMTYPE_FROM_VPPOUT;
-
- ret = ff_filter_init_hw_frames(ctx, outlink, 32);
- if (ret < 0)
- return ret;
-
- ret = av_hwframe_ctx_init(outlink->hw_frames_ctx);
- if (ret < 0)
- return ret;
-
- for (i = 0; i < out_frames_hwctx->nb_surfaces; i++) {
- mfxFrameInfo *info = &out_frames_hwctx->surfaces[i].Info;
- info->CropW = out_width;
- info->CropH = out_height;
- }
-
- return 0;
-}
-
-static mfxStatus frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req,
- mfxFrameAllocResponse *resp)
-{
- AVFilterContext *ctx = pthis;
- QSVScaleContext *s = ctx->priv;
-
- if (!(req->Type & MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET) ||
- !(req->Type & (MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_FROM_VPPOUT)) ||
- !(req->Type & MFX_MEMTYPE_EXTERNAL_FRAME))
- return MFX_ERR_UNSUPPORTED;
-
- if (req->Type & MFX_MEMTYPE_FROM_VPPIN) {
- resp->mids = s->mem_ids_in;
- resp->NumFrameActual = s->nb_mem_ids_in;
- } else {
- resp->mids = s->mem_ids_out;
- resp->NumFrameActual = s->nb_mem_ids_out;
- }
-
- return MFX_ERR_NONE;
-}
-
-static mfxStatus frame_free(mfxHDL pthis, mfxFrameAllocResponse *resp)
-{
- return MFX_ERR_NONE;
-}
-
-static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
-{
- return MFX_ERR_UNSUPPORTED;
-}
-
-static mfxStatus frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
-{
- return MFX_ERR_UNSUPPORTED;
-}
-
-static mfxStatus frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl)
-{
- mfxHDLPair *pair_dst = (mfxHDLPair*)hdl;
- mfxHDLPair *pair_src = (mfxHDLPair*)mid;
-
- pair_dst->first = pair_src->first;
-
- if (pair_src->second != (mfxMemId)MFX_INFINITE)
- pair_dst->second = pair_src->second;
- return MFX_ERR_NONE;
-}
-
-static int init_out_session(AVFilterContext *ctx)
-{
-
- QSVScaleContext *s = ctx->priv;
- AVHWFramesContext *in_frames_ctx = (AVHWFramesContext*)ctx->inputs[0]->hw_frames_ctx->data;
- AVHWFramesContext *out_frames_ctx = (AVHWFramesContext*)ctx->outputs[0]->hw_frames_ctx->data;
- AVQSVFramesContext *in_frames_hwctx = in_frames_ctx->hwctx;
- AVQSVFramesContext *out_frames_hwctx = out_frames_ctx->hwctx;
- AVQSVDeviceContext *device_hwctx = in_frames_ctx->device_ctx->hwctx;
-
- int opaque = 0;
-
- mfxHDL handle = NULL;
- mfxHandleType handle_type;
- mfxVersion ver;
- mfxIMPL impl;
- mfxVideoParam par;
- mfxStatus err;
- int i, ret;
-
-#if QSV_HAVE_OPAQUE
- opaque = !!(in_frames_hwctx->frame_type & MFX_MEMTYPE_OPAQUE_FRAME);
-#endif
- s->num_ext_buf = 0;
-
- /* extract the properties of the "master" session given to us */
- err = MFXQueryIMPL(device_hwctx->session, &impl);
- if (err == MFX_ERR_NONE)
- err = MFXQueryVersion(device_hwctx->session, &ver);
- if (err != MFX_ERR_NONE) {
- av_log(ctx, AV_LOG_ERROR, "Error querying the session attributes\n");
- return AVERROR_UNKNOWN;
- }
-
- if (MFX_IMPL_VIA_VAAPI == MFX_IMPL_VIA_MASK(impl)) {
- handle_type = MFX_HANDLE_VA_DISPLAY;
- } else if (MFX_IMPL_VIA_D3D11 == MFX_IMPL_VIA_MASK(impl)) {
- handle_type = MFX_HANDLE_D3D11_DEVICE;
- } else if (MFX_IMPL_VIA_D3D9 == MFX_IMPL_VIA_MASK(impl)) {
- handle_type = MFX_HANDLE_D3D9_DEVICE_MANAGER;
- } else {
- av_log(ctx, AV_LOG_ERROR, "Error unsupported handle type\n");
- return AVERROR_UNKNOWN;
- }
-
- err = MFXVideoCORE_GetHandle(device_hwctx->session, handle_type, &handle);
- if (err < 0)
- return ff_qsvvpp_print_error(ctx, err, "Error getting the session handle");
- else if (err > 0) {
- ff_qsvvpp_print_warning(ctx, err, "Warning in getting the session handle");
- return AVERROR_UNKNOWN;
- }
-
- /* create a "slave" session with those same properties, to be used for
- * actual scaling */
- ret = ff_qsvvpp_create_mfx_session(ctx, device_hwctx->loader, impl, &ver,
- &s->session);
- if (ret)
- return ret;
-
- if (handle) {
- err = MFXVideoCORE_SetHandle(s->session, handle_type, handle);
- if (err != MFX_ERR_NONE)
- return AVERROR_UNKNOWN;
- }
-
- if (QSV_RUNTIME_VERSION_ATLEAST(ver, 1, 25)) {
- err = MFXJoinSession(device_hwctx->session, s->session);
- if (err != MFX_ERR_NONE)
- return AVERROR_UNKNOWN;
- }
-
- memset(&par, 0, sizeof(par));
-
- if (!opaque) {
- mfxFrameAllocator frame_allocator = {
- .pthis = ctx,
- .Alloc = frame_alloc,
- .Lock = frame_lock,
- .Unlock = frame_unlock,
- .GetHDL = frame_get_hdl,
- .Free = frame_free,
- };
-
- s->mem_ids_in = av_calloc(in_frames_hwctx->nb_surfaces,
- sizeof(*s->mem_ids_in));
- if (!s->mem_ids_in)
- return AVERROR(ENOMEM);
- for (i = 0; i < in_frames_hwctx->nb_surfaces; i++)
- s->mem_ids_in[i] = in_frames_hwctx->surfaces[i].Data.MemId;
- s->nb_mem_ids_in = in_frames_hwctx->nb_surfaces;
-
- s->mem_ids_out = av_calloc(out_frames_hwctx->nb_surfaces,
- sizeof(*s->mem_ids_out));
- if (!s->mem_ids_out)
- return AVERROR(ENOMEM);
- for (i = 0; i < out_frames_hwctx->nb_surfaces; i++)
- s->mem_ids_out[i] = out_frames_hwctx->surfaces[i].Data.MemId;
- s->nb_mem_ids_out = out_frames_hwctx->nb_surfaces;
-
- err = MFXVideoCORE_SetFrameAllocator(s->session, &frame_allocator);
- if (err != MFX_ERR_NONE)
- return AVERROR_UNKNOWN;
-
- par.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
- }
-#if QSV_HAVE_OPAQUE
- else {
- s->surface_ptrs_in = av_calloc(in_frames_hwctx->nb_surfaces,
- sizeof(*s->surface_ptrs_in));
- if (!s->surface_ptrs_in)
- return AVERROR(ENOMEM);
- for (i = 0; i < in_frames_hwctx->nb_surfaces; i++)
- s->surface_ptrs_in[i] = in_frames_hwctx->surfaces + i;
- s->nb_surface_ptrs_in = in_frames_hwctx->nb_surfaces;
-
- s->surface_ptrs_out = av_calloc(out_frames_hwctx->nb_surfaces,
- sizeof(*s->surface_ptrs_out));
- if (!s->surface_ptrs_out)
- return AVERROR(ENOMEM);
- for (i = 0; i < out_frames_hwctx->nb_surfaces; i++)
- s->surface_ptrs_out[i] = out_frames_hwctx->surfaces + i;
- s->nb_surface_ptrs_out = out_frames_hwctx->nb_surfaces;
-
- s->opaque_alloc.In.Surfaces = s->surface_ptrs_in;
- s->opaque_alloc.In.NumSurface = s->nb_surface_ptrs_in;
- s->opaque_alloc.In.Type = in_frames_hwctx->frame_type;
-
- s->opaque_alloc.Out.Surfaces = s->surface_ptrs_out;
- s->opaque_alloc.Out.NumSurface = s->nb_surface_ptrs_out;
- s->opaque_alloc.Out.Type = out_frames_hwctx->frame_type;
-
- s->opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION;
- s->opaque_alloc.Header.BufferSz = sizeof(s->opaque_alloc);
-
- s->ext_buffers[s->num_ext_buf++] = (mfxExtBuffer*)&s->opaque_alloc;
-
- par.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
- }
-#endif
-
- memset(&s->scale_conf, 0, sizeof(mfxExtVPPScaling));
- s->scale_conf.Header.BufferId = MFX_EXTBUFF_VPP_SCALING;
- s->scale_conf.Header.BufferSz = sizeof(mfxExtVPPScaling);
- s->scale_conf.ScalingMode = s->mode;
- s->ext_buffers[s->num_ext_buf++] = (mfxExtBuffer*)&s->scale_conf;
- av_log(ctx, AV_LOG_VERBOSE, "Scaling mode: %d\n", s->mode);
-
- par.ExtParam = s->ext_buffers;
- par.NumExtParam = s->num_ext_buf;
-
- par.AsyncDepth = 1; // TODO async
-
- par.vpp.In = in_frames_hwctx->surfaces[0].Info;
- par.vpp.Out = out_frames_hwctx->surfaces[0].Info;
-
- /* Apparently VPP requires the frame rate to be set to some value, otherwise
- * init will fail (probably for the framerate conversion filter). Since we
- * are only doing scaling here, we just invent an arbitrary
- * value */
- par.vpp.In.FrameRateExtN = 25;
- par.vpp.In.FrameRateExtD = 1;
- par.vpp.Out.FrameRateExtN = 25;
- par.vpp.Out.FrameRateExtD = 1;
-
- /* Print input memory mode */
- ff_qsvvpp_print_iopattern(ctx, par.IOPattern & 0x0F, "VPP");
- /* Print output memory mode */
- ff_qsvvpp_print_iopattern(ctx, par.IOPattern & 0xF0, "VPP");
- err = MFXVideoVPP_Init(s->session, &par);
- if (err < 0)
- return ff_qsvvpp_print_error(ctx, err,
- "Error opening the VPP for scaling");
- else if (err > 0) {
- ff_qsvvpp_print_warning(ctx, err,
- "Warning in VPP initialization");
- return AVERROR_UNKNOWN;
- }
-
- return 0;
-}
-
-static int init_scale_session(AVFilterContext *ctx, int in_width, int in_height,
- int out_width, int out_height)
-{
- int ret;
-
- qsvscale_uninit(ctx);
-
- ret = init_out_pool(ctx, out_width, out_height);
- if (ret < 0)
- return ret;
-
- ret = init_out_session(ctx);
- if (ret < 0)
- return ret;
-
- return 0;
+ ff_qsvvpp_close(ctx);
}
static int qsvscale_config_props(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AVFilterLink *inlink = outlink->src->inputs[0];
- QSVScaleContext *s = ctx->priv;
+ QSVScaleContext *s = ctx->priv;
+ QSVVPPParam param = { NULL };
+ mfxExtBuffer *ext_buf[1];
int64_t w, h;
double var_values[VARS_NB], res;
char *expr;
int ret;
+ enum AVPixelFormat in_format;
var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w;
var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h;
@@ -518,7 +177,30 @@ static int qsvscale_config_props(AVFilterLink *outlink)
outlink->w = w;
outlink->h = h;
- ret = init_scale_session(ctx, inlink->w, inlink->h, w, h);
+ if (inlink->format == AV_PIX_FMT_QSV) {
+ if (!inlink->hw_frames_ctx || !inlink->hw_frames_ctx->data)
+ return AVERROR(EINVAL);
+ else
+ in_format = ((AVHWFramesContext*)inlink->hw_frames_ctx->data)->sw_format;
+ } else
+ in_format = inlink->format;
+
+ if (s->format == AV_PIX_FMT_NONE)
+ s->format = in_format;
+
+ outlink->frame_rate = inlink->frame_rate;
+ outlink->time_base = av_inv_q(inlink->frame_rate);
+ param.out_sw_format = s->format;
+
+ param.ext_buf = ext_buf;
+ memset(&s->scale_conf, 0, sizeof(mfxExtVPPScaling));
+ s->scale_conf.Header.BufferId = MFX_EXTBUFF_VPP_SCALING;
+ s->scale_conf.Header.BufferSz = sizeof(mfxExtVPPScaling);
+ s->scale_conf.ScalingMode = s->mode;
+ param.ext_buf[param.num_ext_buf++] = (mfxExtBuffer*)&s->scale_conf;
+ av_log(ctx, AV_LOG_VERBOSE, "Scaling mode: %d\n", s->mode);
+
+ ret = ff_qsvvpp_init(ctx, ¶m);
if (ret < 0)
return ret;
@@ -542,67 +224,12 @@ fail:
static int qsvscale_filter_frame(AVFilterLink *link, AVFrame *in)
{
- AVFilterContext *ctx = link->dst;
- QSVScaleContext *s = ctx->priv;
- AVFilterLink *outlink = ctx->outputs[0];
-
- mfxSyncPoint sync = NULL;
- mfxStatus err;
-
- AVFrame *out = NULL;
- int ret = 0;
-
- out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
- if (!out) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
+ int ret = 0;
+ AVFilterContext *ctx = link->dst;
+ QSVVPPContext *qsv = ctx->priv;
- do {
- err = MFXVideoVPP_RunFrameVPPAsync(s->session,
- (mfxFrameSurface1*)in->data[3],
- (mfxFrameSurface1*)out->data[3],
- NULL, &sync);
- if (err == MFX_WRN_DEVICE_BUSY)
- av_usleep(1);
- } while (err == MFX_WRN_DEVICE_BUSY);
-
- if (err < 0) {
- ret = ff_qsvvpp_print_error(ctx, err, "Error during scaling");
- goto fail;
- }
-
- if (!sync) {
- av_log(ctx, AV_LOG_ERROR, "No sync during scaling\n");
- ret = AVERROR_UNKNOWN;
- goto fail;
- }
-
- do {
- err = MFXVideoCORE_SyncOperation(s->session, sync, 1000);
- } while (err == MFX_WRN_IN_EXECUTION);
- if (err < 0) {
- ret = ff_qsvvpp_print_error(ctx, err, "Error synchronizing the operation");
- goto fail;
- }
-
- ret = av_frame_copy_props(out, in);
- if (ret < 0)
- goto fail;
-
- out->width = outlink->w;
- out->height = outlink->h;
-
- av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den,
- (int64_t)in->sample_aspect_ratio.num * outlink->h * link->w,
- (int64_t)in->sample_aspect_ratio.den * outlink->w * link->h,
- INT_MAX);
-
- av_frame_free(&in);
- return ff_filter_frame(outlink, out);
-fail:
+ ret = ff_qsvvpp_filter_frame(qsv, link, in);
av_frame_free(&in);
- av_frame_free(&out);
return ret;
}
1
0
ffmpeg | branch: master | galinart <artem.galin(a)gmail.com> | Mon Dec 5 20:18:21 2022 +0000| [500282941655558e2440afe163f0268dc5ac61bf] | committer: Haihao Xiang
libavcodec/qsvenc: enable Hyper Encode
Hyper Encode uses Intel integrated and discrete graphics on one system
to accelerate encoding of a single video stream.
Depending on the selected parameters and codecs, performance gain on AlderLake iGPU + ARC Gfx up to 1.6x.
More information: https://www.intel.co.uk/content/www/uk/en/architecture-and-technology/adapt…
Developer guide: https://github.com/oneapi-src/oneVPL-intel-gpu/blob/main/doc/HyperEncode_Fe…
Hyper Encode is supported only on Windows and requires D3D11 and oneVPL.
To enable Hyper Encode need to specify:
-Hyper Encode mode (-dual_gfx on or dual_gfx adaptive)
-Encoder: h264_qsv or hevc_qsv
-BRC: VBR, CQP or ICQ
-Lowpower mode (-low_power 1)
-Closed GOP for AVC or strict GOP for HEVC -idr_interval = 0 used by default
Depending on the encoding parameters, the following parameters may need
to be adjusted:
-g recommended >= 30 for better performance
-async_depth recommended >= 30 for better performance
-extra_hw_frames recommended equal to async_depth value
-bf recommended = 0 for better performance
In the cases with fast encoding (-preset veryfast) there may be no
performance gain due to the fact that the decode is slower than the encode.
Command line examples:
ffmpeg.exe -init_hw_device qsv:hw,child_device_type=d3d11va,child_device=0 -v verbose -y -hwaccel qsv -extra_hw_frames 60 -async_depth 60 -c:v h264_qsv -i bbb_sunflower_2160p_60fps_normal.mp4
-async_depth 60 -c:v h264_qsv -preset medium -g 60 -low_power 1 -bf 0 -dual_gfx on output.h265
Signed-off-by: galinart <artem.galin(a)intel.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=500282941655558e2…
---
libavcodec/qsvenc.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++--
libavcodec/qsvenc.h | 19 ++++++++--
libavcodec/qsvenc_h264.c | 3 ++
libavcodec/qsvenc_hevc.c | 3 ++
4 files changed, 112 insertions(+), 4 deletions(-)
diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
index 93f1862a4b..2f0e94a914 100644
--- a/libavcodec/qsvenc.c
+++ b/libavcodec/qsvenc.c
@@ -169,6 +169,8 @@ do { \
} \
} while (0) \
+#define MFX_IMPL_VIA_MASK(impl) (0x0f00 & (impl))
+
static const char *print_ratecontrol(mfxU16 rc_mode)
{
int i;
@@ -197,6 +199,10 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q,
mfxExtCodingOption2 *co2 = NULL;
mfxExtCodingOption3 *co3 = NULL;
mfxExtHEVCTiles *exthevctiles = NULL;
+#if QSV_HAVE_HE
+ mfxExtHyperModeParam *exthypermodeparam = NULL;
+#endif
+
const char *tmp_str = NULL;
if (q->co2_idx > 0)
@@ -208,6 +214,11 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q,
if (q->exthevctiles_idx > 0)
exthevctiles = (mfxExtHEVCTiles *)coding_opts[q->exthevctiles_idx];
+#if QSV_HAVE_HE
+ if (q->exthypermodeparam_idx > 0)
+ exthypermodeparam = (mfxExtHyperModeParam *)coding_opts[q->exthypermodeparam_idx];
+#endif
+
av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n",
print_profile(avctx->codec_id, info->CodecProfile), info->CodecLevel);
@@ -373,6 +384,21 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q,
av_log(avctx, AV_LOG_VERBOSE, "NumTileColumns: %"PRIu16"; NumTileRows: %"PRIu16"\n",
exthevctiles->NumTileColumns, exthevctiles->NumTileRows);
}
+
+#if QSV_HAVE_HE
+ if (exthypermodeparam) {
+ av_log(avctx, AV_LOG_VERBOSE, "HyperEncode: ");
+
+ if (exthypermodeparam->Mode == MFX_HYPERMODE_OFF)
+ av_log(avctx, AV_LOG_VERBOSE, "OFF");
+ if (exthypermodeparam->Mode == MFX_HYPERMODE_ON)
+ av_log(avctx, AV_LOG_VERBOSE, "ON");
+ if (exthypermodeparam->Mode == MFX_HYPERMODE_ADAPTIVE)
+ av_log(avctx, AV_LOG_VERBOSE, "Adaptive");
+
+ av_log(avctx, AV_LOG_VERBOSE, "\n");
+ }
+#endif
}
static void dump_video_vp9_param(AVCodecContext *avctx, QSVEncContext *q,
@@ -1168,6 +1194,54 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extvsi;
}
+#if QSV_HAVE_HE
+ if (q->dual_gfx) {
+ if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 2, 4)) {
+ mfxIMPL impl;
+ MFXQueryIMPL(q->session, &impl);
+
+ if (MFX_IMPL_VIA_MASK(impl) != MFX_IMPL_VIA_D3D11) {
+ av_log(avctx, AV_LOG_ERROR, "Dual GFX mode requires D3D11VA \n");
+ return AVERROR_UNKNOWN;
+ }
+ if (q->param.mfx.LowPower != MFX_CODINGOPTION_ON) {
+ av_log(avctx, AV_LOG_ERROR, "Dual GFX mode supports only low-power encoding mode \n");
+ return AVERROR_UNKNOWN;
+ }
+ if (q->param.mfx.CodecId != MFX_CODEC_AVC && q->param.mfx.CodecId != MFX_CODEC_HEVC) {
+ av_log(avctx, AV_LOG_ERROR, "Not supported encoder for dual GFX mode. "
+ "Supported: h264_qsv and hevc_qsv \n");
+ return AVERROR_UNKNOWN;
+ }
+ if (q->param.mfx.RateControlMethod != MFX_RATECONTROL_VBR &&
+ q->param.mfx.RateControlMethod != MFX_RATECONTROL_CQP &&
+ q->param.mfx.RateControlMethod != MFX_RATECONTROL_ICQ) {
+ av_log(avctx, AV_LOG_WARNING, "Not supported BRC for dual GFX mode. "
+ "Supported: VBR, CQP and ICQ \n");
+ }
+ if ((q->param.mfx.CodecId == MFX_CODEC_AVC && q->param.mfx.IdrInterval != 0) ||
+ (q->param.mfx.CodecId == MFX_CODEC_HEVC && q->param.mfx.IdrInterval != 1)) {
+ av_log(avctx, AV_LOG_WARNING, "Dual GFX mode requires closed GOP for AVC and strict GOP for HEVC, -idr_interval 0 \n");
+ }
+ if (q->param.mfx.GopPicSize < 30) {
+ av_log(avctx, AV_LOG_WARNING, "For better performance in dual GFX mode GopPicSize must be >= 30 \n");
+ }
+ if (q->param.AsyncDepth < 30) {
+ av_log(avctx, AV_LOG_WARNING, "For better performance in dual GFX mode AsyncDepth must be >= 30 \n");
+ }
+
+ q->exthypermodeparam.Header.BufferId = MFX_EXTBUFF_HYPER_MODE_PARAM;
+ q->exthypermodeparam.Header.BufferSz = sizeof(q->exthypermodeparam);
+ q->exthypermodeparam.Mode = q->dual_gfx;
+ q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->exthypermodeparam;
+ } else {
+ av_log(avctx, AV_LOG_ERROR,
+ "This version of runtime doesn't support Hyper Encode\n");
+ return AVERROR_UNKNOWN;
+ }
+ }
+#endif
+
if (!check_enc_param(avctx,q)) {
av_log(avctx, AV_LOG_ERROR,
"some encoding parameters are not supported by the QSV "
@@ -1342,12 +1416,19 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q)
.Header.BufferSz = sizeof(hevc_tile_buf),
};
- mfxExtBuffer *ext_buffers[6];
+#if QSV_HAVE_HE
+ mfxExtHyperModeParam hyper_mode_param_buf = {
+ .Header.BufferId = MFX_EXTBUFF_HYPER_MODE_PARAM,
+ .Header.BufferSz = sizeof(hyper_mode_param_buf),
+ };
+#endif
+
+ mfxExtBuffer *ext_buffers[6 + QSV_HAVE_HE];
int need_pps = avctx->codec_id != AV_CODEC_ID_MPEG2VIDEO;
int ret, ext_buf_num = 0, extradata_offset = 0;
- q->co2_idx = q->co3_idx = q->exthevctiles_idx = -1;
+ q->co2_idx = q->co3_idx = q->exthevctiles_idx = q->exthypermodeparam_idx = -1;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&extradata;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co;
@@ -1369,6 +1450,12 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q)
q->exthevctiles_idx = ext_buf_num;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&hevc_tile_buf;
}
+#if QSV_HAVE_HE
+ if (q->dual_gfx && QSV_RUNTIME_VERSION_ATLEAST(q->ver, 2, 4)) {
+ q->exthypermodeparam_idx = ext_buf_num;
+ ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&hyper_mode_param_buf;
+ }
+#endif
q->param.ExtParam = ext_buffers;
q->param.NumExtParam = ext_buf_num;
diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h
index a7bbb3797e..4a6fa2caed 100644
--- a/libavcodec/qsvenc.h
+++ b/libavcodec/qsvenc.h
@@ -45,10 +45,12 @@
#define QSV_HAVE_AVBR 1
#define QSV_HAVE_VCM 1
#define QSV_HAVE_MF 0
+#define QSV_HAVE_HE QSV_VERSION_ATLEAST(2, 4)
#else
#define QSV_HAVE_AVBR 0
#define QSV_HAVE_VCM 0
#define QSV_HAVE_MF !QSV_ONEVPL
+#define QSV_HAVE_HE 0
#endif
#define QSV_COMMON_OPTS \
@@ -64,6 +66,14 @@
{ "forced_idr", "Forcing I frames as IDR frames", OFFSET(qsv.forced_idr), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, \
{ "low_power", "enable low power mode(experimental: many limitations by mfx version, BRC modes, etc.)", OFFSET(qsv.low_power), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, VE},
+#if QSV_HAVE_HE
+#define QSV_HE_OPTIONS \
+{ "dual_gfx", "Prefer processing on both iGfx and dGfx simultaneously", OFFSET(qsv.dual_gfx), AV_OPT_TYPE_INT, { .i64 = MFX_HYPERMODE_OFF }, MFX_HYPERMODE_OFF, MFX_HYPERMODE_ADAPTIVE, VE, "dual_gfx" }, \
+{ "off", "Disable HyperEncode mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_HYPERMODE_OFF }, INT_MIN, INT_MAX, VE, "dual_gfx" }, \
+{ "on", "Enable HyperEncode mode and return error if incompatible parameters during initialization", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_HYPERMODE_ON }, INT_MIN, INT_MAX, VE, "dual_gfx" }, \
+{ "adaptive", "Enable HyperEncode mode or fallback to single GPU if incompatible parameters during initialization", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_HYPERMODE_ADAPTIVE }, INT_MIN, INT_MAX, VE, "dual_gfx" },
+#endif
+
#define QSV_OPTION_RDO \
{ "rdo", "Enable rate distortion optimization", OFFSET(qsv.rdo), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE },
@@ -171,7 +181,9 @@ typedef struct QSVEncContext {
mfxExtAV1TileParam extav1tileparam;
mfxExtAV1BitstreamParam extav1bsparam;
#endif
-
+#if QSV_HAVE_HE
+ mfxExtHyperModeParam exthypermodeparam;
+#endif
#if QSV_HAVE_OPAQUE
mfxExtOpaqueSurfaceAlloc opaque_alloc;
mfxFrameSurface1 **opaque_surfaces;
@@ -180,7 +192,7 @@ typedef struct QSVEncContext {
mfxExtVideoSignalInfo extvsi;
- mfxExtBuffer *extparam_internal[5 + (QSV_HAVE_MF * 2) + QSV_HAVE_EXT_AV1_PARAM * 2];
+ mfxExtBuffer *extparam_internal[5 + (QSV_HAVE_MF * 2) + (QSV_HAVE_EXT_AV1_PARAM * 2) + QSV_HAVE_HE];
int nb_extparam_internal;
mfxExtBuffer **extparam;
@@ -255,6 +267,7 @@ typedef struct QSVEncContext {
int co2_idx;
int co3_idx;
int exthevctiles_idx;
+ int exthypermodeparam_idx;
int vp9_idx;
int max_qp_i;
@@ -299,6 +312,8 @@ typedef struct QSVEncContext {
// This is used for SEI Timing reset
int old_pic_timing_sei;
int skip_frame;
+ // This is used for Hyper Encode
+ int dual_gfx;
} QSVEncContext;
int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q);
diff --git a/libavcodec/qsvenc_h264.c b/libavcodec/qsvenc_h264.c
index bbdedc3335..071a9a79e9 100644
--- a/libavcodec/qsvenc_h264.c
+++ b/libavcodec/qsvenc_h264.c
@@ -117,6 +117,9 @@ static const AVOption options[] = {
QSV_OPTION_SCENARIO
QSV_OPTION_AVBR
QSV_OPTION_SKIP_FRAME
+#if QSV_HAVE_HE
+ QSV_HE_OPTIONS
+#endif
{ "cavlc", "Enable CAVLC", OFFSET(qsv.cavlc), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
#if QSV_HAVE_VCM
diff --git a/libavcodec/qsvenc_hevc.c b/libavcodec/qsvenc_hevc.c
index e042263bf5..5e23ca9647 100644
--- a/libavcodec/qsvenc_hevc.c
+++ b/libavcodec/qsvenc_hevc.c
@@ -318,6 +318,9 @@ static const AVOption options[] = {
QSV_OPTION_SCENARIO
QSV_OPTION_AVBR
QSV_OPTION_SKIP_FRAME
+#if QSV_HAVE_HE
+ QSV_HE_OPTIONS
+#endif
{ "idr_interval", "Distance (in I-frames) between IDR frames", OFFSET(qsv.idr_interval), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT_MAX, VE, "idr_interval" },
{ "begin_only", "Output an IDR-frame only at the beginning of the stream", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, "idr_interval" },
1
0
ffmpeg | branch: master | Haihao Xiang <haihao.xiang(a)intel.com> | Thu Jan 19 16:30:06 2023 +0800| [aecfec6f80fd8be68e050a97935589beeae588fa] | committer: Haihao Xiang
avfilter: add VA-API variants of the stack filters
Include hstack_vaapi, vstack_vaapi and xstack_vaapi. They may accept
input streams with different sizes. libva2 (VA-API 1.0+) is required.
Example:
$ ffmpeg -hwaccel vaapi -hwaccel_output_format vaapi -i input.h265 -filter_complex
"[0:v][0:v]hstack_vaapi" -c:v h264_vaapi out.h264
$ ffmpeg \
-hwaccel vaapi -hwaccel_output_format vaapi -i input.h264 \
-hwaccel vaapi -hwaccel_output_format vaapi -i input.h264 \
-hwaccel vaapi -hwaccel_output_format vaapi -i input.h264 \
-hwaccel vaapi -hwaccel_output_format vaapi -i input.h264 \
-filter_complex "[0:v][1:v][2:v][3:v]xstack_vaapi=inputs=4:fill=0x000000:layout=0_0_1920x1080|w0_0_1920x1080|0_h0_1920x1080|w0_h0_1920x1080" \
-c:v hevc_vaapi out.h265
Signed-off-by: Haihao Xiang <haihao.xiang(a)intel.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=aecfec6f80fd8be68…
---
Changelog | 1 +
configure | 3 +
doc/filters.texi | 81 ++++++
libavfilter/Makefile | 3 +
libavfilter/allfilters.c | 3 +
libavfilter/version.h | 2 +-
libavfilter/vf_stack_vaapi.c | 574 +++++++++++++++++++++++++++++++++++++++++++
7 files changed, 666 insertions(+), 1 deletion(-)
diff --git a/Changelog b/Changelog
index e07a039ca4..a0f1ad7211 100644
--- a/Changelog
+++ b/Changelog
@@ -33,6 +33,7 @@ version <next>:
- CBD2 DPCM decoder
- ssim360 video filter
- ffmpeg CLI new options: -enc_stats_pre[_fmt], -enc_stats_post[_fmt]
+- hstack_vaapi, vstack_vaapi and xstack_vaapi filters
version 5.1:
diff --git a/configure b/configure
index cb895cf9ac..8190e978a8 100755
--- a/configure
+++ b/configure
@@ -3766,6 +3766,9 @@ xfade_opencl_filter_deps="opencl"
yadif_cuda_filter_deps="ffnvcodec"
yadif_cuda_filter_deps_any="cuda_nvcc cuda_llvm"
yadif_videotoolbox_filter_deps="metal corevideo videotoolbox"
+hstack_vaapi_filter_deps="vaapi_1"
+vstack_vaapi_filter_deps="vaapi_1"
+xstack_vaapi_filter_deps="vaapi_1"
# examples
avio_list_dir_deps="avformat avutil"
diff --git a/doc/filters.texi b/doc/filters.texi
index 4f4d2ea807..3a54c68f3e 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -14941,6 +14941,7 @@ Set the scaling dimension: @code{2} for @code{hq2x}, @code{3} for
Default is @code{3}.
@end table
+@anchor{hstack}
@section hstack
Stack input videos horizontally.
@@ -24392,6 +24393,7 @@ Example:
ffmpeg -i ref.mpg -vf vmafmotion -f null -
@end example
+@anchor{vstack}
@section vstack
Stack input videos vertically.
@@ -24873,6 +24875,7 @@ minimum values, and @code{1} maximum values.
This filter supports all above options as @ref{commands}, excluding option @code{inputs}.
+@anchor{xstack}
@section xstack
Stack video inputs into custom layout.
@@ -26689,6 +26692,84 @@ tonemap_vaapi=format=p010:t=bt2020-10
@end example
@end itemize
+@section hstack_vaapi
+Stack input videos horizontally.
+
+This is the VA-API variant of the @ref{hstack} filter, each input stream may
+have different height, this filter will scale down/up each input stream while
+keeping the orignal aspect.
+
+It accepts the following options:
+
+@table @option
+@item inputs
+See @ref{hstack}.
+
+@item shortest
+See @ref{hstack}.
+
+@item height
+Set height of output. If set to 0, this filter will set height of output to
+height of the first input stream. Default value is 0.
+@end table
+
+@section vstack_vaapi
+Stack input videos vertically.
+
+This is the VA-API variant of the @ref{vstack} filter, each input stream may
+have different width, this filter will scale down/up each input stream while
+keeping the orignal aspect.
+
+It accepts the following options:
+
+@table @option
+@item inputs
+See @ref{vstack}.
+
+@item shortest
+See @ref{vstack}.
+
+@item width
+Set width of output. If set to 0, this filter will set width of output to
+width of the first input stream. Default value is 0.
+@end table
+
+@section xstack_vaapi
+Stack video inputs into custom layout.
+
+This is the VA-API variant of the @ref{xstack} filter, each input stream may
+have different size, this filter will scale down/up each input stream to the
+given output size, or the size of the first input stream.
+
+It accepts the following options:
+
+@table @option
+@item inputs
+See @ref{xstack}.
+
+@item shortest
+See @ref{xstack}.
+
+@item layout
+See @ref{xstack}.
+Moreover, this permits the user to supply output size for each input stream.
+@example
+xstack_vaapi=inputs=4:layout=0_0_1920x1080|0_h0_1920x1080|w0_0_1920x1080|w0_h0_1920x1080
+@end example
+
+@item grid
+See @ref{xstack}.
+
+@item grid_tile_size
+Set output size for each input stream when @option{grid} is set. If this option
+is not set, this filter will set output size by default to the size of the
+first input stream. For the syntax of this option, check the
+@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}.
+
+@item fill
+See @ref{xstack}.
+@end table
+
@c man end VAAPI VIDEO FILTERS
@chapter Video Sources
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index d4e38bd4e8..a0059c69e6 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -558,6 +558,9 @@ OBJS-$(CONFIG_YAEPBLUR_FILTER) += vf_yaepblur.o
OBJS-$(CONFIG_ZMQ_FILTER) += f_zmq.o
OBJS-$(CONFIG_ZOOMPAN_FILTER) += vf_zoompan.o
OBJS-$(CONFIG_ZSCALE_FILTER) += vf_zscale.o
+OBJS-$(CONFIG_HSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o
+OBJS-$(CONFIG_VSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o
+OBJS-$(CONFIG_XSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o
OBJS-$(CONFIG_ALLRGB_FILTER) += vsrc_testsrc.o
OBJS-$(CONFIG_ALLYUV_FILTER) += vsrc_testsrc.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 5c86d0789f..9cdcca4853 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -523,6 +523,9 @@ extern const AVFilter ff_vf_yaepblur;
extern const AVFilter ff_vf_zmq;
extern const AVFilter ff_vf_zoompan;
extern const AVFilter ff_vf_zscale;
+extern const AVFilter ff_vf_hstack_vaapi;
+extern const AVFilter ff_vf_vstack_vaapi;
+extern const AVFilter ff_vf_xstack_vaapi;
extern const AVFilter ff_vsrc_allrgb;
extern const AVFilter ff_vsrc_allyuv;
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 09b9954597..057ab63415 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -31,7 +31,7 @@
#include "version_major.h"
-#define LIBAVFILTER_VERSION_MINOR 55
+#define LIBAVFILTER_VERSION_MINOR 56
#define LIBAVFILTER_VERSION_MICRO 100
diff --git a/libavfilter/vf_stack_vaapi.c b/libavfilter/vf_stack_vaapi.c
new file mode 100644
index 0000000000..403fbb19eb
--- /dev/null
+++ b/libavfilter/vf_stack_vaapi.c
@@ -0,0 +1,574 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Hardware accelerated hstack, vstack and xstack filters based on VA-API
+ */
+
+#include "config_components.h"
+
+#include "libavutil/opt.h"
+#include "libavutil/common.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/eval.h"
+#include "libavutil/hwcontext.h"
+#include "libavutil/avstring.h"
+#include "libavutil/avassert.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/mem.h"
+
+#include "internal.h"
+#include "filters.h"
+#include "formats.h"
+#include "video.h"
+#include "framesync.h"
+#include "vaapi_vpp.h"
+
+#define OFFSET(x) offsetof(StackVAAPIContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+enum {
+ STACK_VAAPI_H = 0,
+ STACK_VAAPI_V = 1,
+ STACK_VAAPI_X = 2
+};
+
+typedef struct StackVAAPIContext {
+ VAAPIVPPContext vppctx; /**< must be the first field */
+
+ FFFrameSync fs;
+ int mode;
+ VARectangle *rects;
+ uint8_t fillcolor[4];
+ int fillcolor_enable;
+
+ /* Options */
+ int nb_inputs;
+ int shortest;
+ int tile_width;
+ int tile_height;
+ int nb_grid_columns;
+ int nb_grid_rows;
+ char *layout;
+ char *fillcolor_str;
+} StackVAAPIContext;
+
+static int process_frame(FFFrameSync *fs)
+{
+ AVFilterContext *avctx = fs->parent;
+ AVFilterLink *outlink = avctx->outputs[0];
+ StackVAAPIContext *sctx = fs->opaque;
+ VAAPIVPPContext *vppctx = fs->opaque;
+ AVFrame *oframe, *iframe;
+ VAProcPipelineParameterBuffer *params = NULL;
+ VARectangle *irect = NULL;
+ int ret = 0;
+
+ if (vppctx->va_context == VA_INVALID_ID)
+ return AVERROR(EINVAL);
+
+ oframe = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+ if (!oframe)
+ return AVERROR(ENOMEM);
+
+ irect = av_calloc(avctx->nb_inputs, sizeof(*irect));
+ params = av_calloc(avctx->nb_inputs, sizeof(*params));
+ if (!irect || !params) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ for (int i = 0; i < avctx->nb_inputs; i++) {
+ ret = ff_framesync_get_frame(fs, i, &iframe, 0);
+ if (ret)
+ goto fail;
+
+ if (i == 0) {
+ ret = av_frame_copy_props(oframe, iframe);
+ if (ret < 0)
+ goto fail;
+ }
+
+ ret = ff_vaapi_vpp_init_params(avctx, ¶ms[i], iframe, oframe);
+ if (ret)
+ goto fail;
+
+ av_log(avctx, AV_LOG_DEBUG, "stack input %d: %s, %ux%u (%"PRId64").\n",
+ i, av_get_pix_fmt_name(iframe->format),
+ iframe->width, iframe->height, iframe->pts);
+ irect[i].x = 0;
+ irect[i].y = 0;
+ irect[i].width = iframe->width;
+ irect[i].height = iframe->height;
+ params[i].surface_region = &irect[i];
+ params[i].surface = (VASurfaceID)(uintptr_t)iframe->data[3];
+ params[i].output_region = &sctx->rects[i];
+
+ if (sctx->fillcolor_enable)
+ params[i].output_background_color = (sctx->fillcolor[3] << 24 |
+ sctx->fillcolor[0] << 16 |
+ sctx->fillcolor[1] << 8 |
+ sctx->fillcolor[2]);
+ }
+
+ oframe->pts = av_rescale_q(sctx->fs.pts, sctx->fs.time_base, outlink->time_base);
+ oframe->sample_aspect_ratio = outlink->sample_aspect_ratio;
+
+ ret = ff_vaapi_vpp_render_pictures(avctx, params, avctx->nb_inputs, oframe);
+ if (ret)
+ goto fail;
+
+ av_freep(&irect);
+ av_freep(¶ms);
+ return ff_filter_frame(outlink, oframe);
+
+fail:
+ av_freep(&irect);
+ av_freep(¶ms);
+ av_frame_free(&oframe);
+ return ret;
+}
+
+static int init_framesync(AVFilterContext *avctx)
+{
+ StackVAAPIContext *sctx = avctx->priv;
+ int ret;
+
+ ret = ff_framesync_init(&sctx->fs, avctx, avctx->nb_inputs);
+ if (ret < 0)
+ return ret;
+
+ sctx->fs.on_event = process_frame;
+ sctx->fs.opaque = sctx;
+
+ for (int i = 0; i < sctx->nb_inputs; i++) {
+ FFFrameSyncIn *in = &sctx->fs.in[i];
+
+ in->before = EXT_STOP;
+ in->after = sctx->shortest ? EXT_STOP : EXT_INFINITY;
+ in->sync = 1;
+ in->time_base = avctx->inputs[i]->time_base;
+ }
+
+ return ff_framesync_configure(&sctx->fs);
+}
+
+#define SET_INPUT_REGION(rect, rx, ry, rw, rh) do { \
+ rect->x = rx; \
+ rect->y = ry; \
+ rect->width = rw; \
+ rect->height = rh; \
+ } while (0)
+
+static int config_output(AVFilterLink *outlink)
+{
+ AVFilterContext *avctx = outlink->src;
+ StackVAAPIContext *sctx = avctx->priv;
+ VAAPIVPPContext *vppctx = avctx->priv;
+ AVFilterLink *inlink0 = avctx->inputs[0];
+ AVHWFramesContext *hwfc0 = NULL;
+ int width, height, ret;
+
+ if (inlink0->format != AV_PIX_FMT_VAAPI || !inlink0->hw_frames_ctx || !inlink0->hw_frames_ctx->data) {
+ av_log(avctx, AV_LOG_ERROR, "Software pixel format is not supported.\n");
+ return AVERROR(EINVAL);
+ }
+
+ hwfc0 = (AVHWFramesContext *)inlink0->hw_frames_ctx->data;
+
+ for (int i = 1; i < sctx->nb_inputs; i++) {
+ AVFilterLink *inlink = avctx->inputs[i];
+ AVHWFramesContext *hwfc = NULL;
+
+ if (inlink->format != AV_PIX_FMT_VAAPI || !inlink->hw_frames_ctx || !inlink->hw_frames_ctx->data) {
+ av_log(avctx, AV_LOG_ERROR, "Software pixel format is not supported.\n");
+ return AVERROR(EINVAL);
+ }
+
+ hwfc = (AVHWFramesContext *)inlink->hw_frames_ctx->data;
+
+ if (hwfc0->sw_format != hwfc->sw_format) {
+ av_log(avctx, AV_LOG_ERROR, "All inputs should have the same underlying software pixel format.\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (hwfc0->device_ctx != hwfc->device_ctx) {
+ av_log(avctx, AV_LOG_ERROR, "All inputs should have the same underlying vaapi devices.\n");
+ return AVERROR(EINVAL);
+ }
+ }
+
+ ff_vaapi_vpp_config_input(inlink0);
+ vppctx->output_format = hwfc0->sw_format;
+
+ if (sctx->mode == STACK_VAAPI_H) {
+ height = sctx->tile_height;
+ width = 0;
+
+ if (!height)
+ height = inlink0->h;
+
+ for (int i = 0; i < sctx->nb_inputs; i++) {
+ AVFilterLink *inlink = avctx->inputs[i];
+ VARectangle *rect = &sctx->rects[i];
+
+ SET_INPUT_REGION(rect, width, 0, av_rescale(height, inlink->w, inlink->h), height);
+ width += av_rescale(height, inlink->w, inlink->h);
+ }
+ } else if (sctx->mode == STACK_VAAPI_V) {
+ height = 0;
+ width = sctx->tile_width;
+
+ if (!width)
+ width = inlink0->w;
+
+ for (int i = 0; i < sctx->nb_inputs; i++) {
+ AVFilterLink *inlink = avctx->inputs[i];
+ VARectangle *rect = &sctx->rects[i];
+
+ SET_INPUT_REGION(rect, 0, height, width, av_rescale(width, inlink->h, inlink->w));
+ height += av_rescale(width, inlink->h, inlink->w);
+ }
+ } else if (sctx->nb_grid_rows && sctx->nb_grid_columns) {
+ int xpos = 0, ypos = 0;
+ int ow, oh, k = 0;
+
+ ow = sctx->tile_width;
+ oh = sctx->tile_height;
+
+ if (!ow || !oh) {
+ ow = avctx->inputs[0]->w;
+ oh = avctx->inputs[0]->h;
+ }
+
+ for (int i = 0; i < sctx->nb_grid_columns; i++) {
+ ypos = 0;
+
+ for (int j = 0; j < sctx->nb_grid_rows; j++) {
+ VARectangle *rect = &sctx->rects[k++];
+
+ SET_INPUT_REGION(rect, xpos, ypos, ow, oh);
+ ypos += oh;
+ }
+
+ xpos += ow;
+ }
+
+ width = ow * sctx->nb_grid_columns;
+ height = oh * sctx->nb_grid_rows;
+ } else {
+ char *arg, *p = sctx->layout, *saveptr = NULL;
+ char *arg2, *p2, *saveptr2 = NULL;
+ char *arg3, *p3, *saveptr3 = NULL;
+ int xpos, ypos, size;
+ int ow, oh;
+
+ width = avctx->inputs[0]->w;
+ height = avctx->inputs[0]->h;
+
+ for (int i = 0; i < sctx->nb_inputs; i++) {
+ AVFilterLink *inlink = avctx->inputs[i];
+ VARectangle *rect = &sctx->rects[i];
+
+ ow = inlink->w;
+ oh = inlink->h;
+
+ if (!(arg = av_strtok(p, "|", &saveptr)))
+ return AVERROR(EINVAL);
+
+ p = NULL;
+ p2 = arg;
+ xpos = ypos = 0;
+
+ for (int j = 0; j < 3; j++) {
+ if (!(arg2 = av_strtok(p2, "_", &saveptr2))) {
+ if (j == 2)
+ break;
+ else
+ return AVERROR(EINVAL);
+ }
+
+ p2 = NULL;
+ p3 = arg2;
+
+ if (j == 2) {
+ if ((ret = av_parse_video_size(&ow, &oh, p3)) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid size '%s'\n", p3);
+ return ret;
+ }
+
+ break;
+ }
+
+ while ((arg3 = av_strtok(p3, "+", &saveptr3))) {
+ p3 = NULL;
+ if (sscanf(arg3, "w%d", &size) == 1) {
+ if (size == i || size < 0 || size >= sctx->nb_inputs)
+ return AVERROR(EINVAL);
+
+ if (!j)
+ xpos += sctx->rects[size].width;
+ else
+ ypos += sctx->rects[size].width;
+ } else if (sscanf(arg3, "h%d", &size) == 1) {
+ if (size == i || size < 0 || size >= sctx->nb_inputs)
+ return AVERROR(EINVAL);
+
+ if (!j)
+ xpos += sctx->rects[size].height;
+ else
+ ypos += sctx->rects[size].height;
+ } else if (sscanf(arg3, "%d", &size) == 1) {
+ if (size < 0)
+ return AVERROR(EINVAL);
+
+ if (!j)
+ xpos += size;
+ else
+ ypos += size;
+ } else {
+ return AVERROR(EINVAL);
+ }
+ }
+ }
+
+ SET_INPUT_REGION(rect, xpos, ypos, ow, oh);
+ width = FFMAX(width, xpos + ow);
+ height = FFMAX(height, ypos + oh);
+ }
+
+ }
+
+ outlink->w = width;
+ outlink->h = height;
+ outlink->frame_rate = inlink0->frame_rate;
+ outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
+
+ for (int i = 1; i < sctx->nb_inputs; i++) {
+ AVFilterLink *inlink = avctx->inputs[i];
+ if (outlink->frame_rate.num != inlink->frame_rate.num ||
+ outlink->frame_rate.den != inlink->frame_rate.den) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "Video inputs have different frame rates, output will be VFR\n");
+ outlink->frame_rate = av_make_q(1, 0);
+ break;
+ }
+ }
+
+ ret = init_framesync(avctx);
+ if (ret < 0)
+ return ret;
+
+ outlink->time_base = sctx->fs.time_base;
+
+ vppctx->output_width = width;
+ vppctx->output_height = height;
+
+ return ff_vaapi_vpp_config_output(outlink);
+}
+
+static int vaapi_stack_init(AVFilterContext *avctx)
+{
+ StackVAAPIContext *sctx = avctx->priv;
+ VAAPIVPPContext *vppctx = avctx->priv;
+ int ret;
+
+ if (!strcmp(avctx->filter->name, "hstack_vaapi"))
+ sctx->mode = STACK_VAAPI_H;
+ else if (!strcmp(avctx->filter->name, "vstack_vaapi"))
+ sctx->mode = STACK_VAAPI_V;
+ else {
+ int is_grid;
+
+ av_assert0(strcmp(avctx->filter->name, "xstack_vaapi") == 0);
+ sctx->mode = STACK_VAAPI_X;
+ is_grid = sctx->nb_grid_rows && sctx->nb_grid_columns;
+
+ if (sctx->layout && is_grid) {
+ av_log(avctx, AV_LOG_ERROR, "Both layout and grid were specified. Only one is allowed.\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (!sctx->layout && !is_grid) {
+ if (sctx->nb_inputs == 2) {
+ sctx->nb_grid_rows = 1;
+ sctx->nb_grid_columns = 2;
+ is_grid = 1;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "No layout or grid specified.\n");
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (is_grid)
+ sctx->nb_inputs = sctx->nb_grid_rows * sctx->nb_grid_columns;
+
+ if (strcmp(sctx->fillcolor_str, "none") &&
+ av_parse_color(sctx->fillcolor, sctx->fillcolor_str, -1, avctx) >= 0) {
+ sctx->fillcolor_enable = 1;
+ } else {
+ sctx->fillcolor_enable = 0;
+ }
+ }
+
+ for (int i = 0; i < sctx->nb_inputs; i++) {
+ AVFilterPad pad = { 0 };
+
+ pad.type = AVMEDIA_TYPE_VIDEO;
+ pad.name = av_asprintf("input%d", i);
+
+ if (!pad.name)
+ return AVERROR(ENOMEM);
+
+ if ((ret = ff_append_inpad_free_name(avctx, &pad)) < 0)
+ return ret;
+ }
+
+ /* stack region */
+ sctx->rects = av_calloc(sctx->nb_inputs, sizeof(*sctx->rects));
+ if (!sctx->rects)
+ return AVERROR(ENOMEM);
+
+ ff_vaapi_vpp_ctx_init(avctx);
+ vppctx->output_format = AV_PIX_FMT_NONE;
+
+ return 0;
+}
+
+static av_cold void vaapi_stack_uninit(AVFilterContext *avctx)
+{
+ StackVAAPIContext *sctx = avctx->priv;
+
+ ff_framesync_uninit(&sctx->fs);
+ av_freep(&sctx->rects);
+}
+
+static int vaapi_stack_activate(AVFilterContext *avctx)
+{
+ StackVAAPIContext *sctx = avctx->priv;
+ return ff_framesync_activate(&sctx->fs);
+}
+
+static int vaapi_stack_query_formats(AVFilterContext *avctx)
+{
+ static const enum AVPixelFormat pixel_formats[] = {
+ AV_PIX_FMT_VAAPI,
+ AV_PIX_FMT_NONE,
+ };
+
+ return ff_set_common_formats_from_list(avctx, pixel_formats);
+}
+
+static const AVFilterPad vaapi_stack_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_output,
+ },
+};
+
+#define STACK_COMMON_OPTS \
+ { "inputs", "Set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 2, UINT16_MAX, .flags = FLAGS }, \
+ { "shortest", "Force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+
+#if CONFIG_HSTACK_VAAPI_FILTER
+
+static const AVOption hstack_vaapi_options[] = {
+ STACK_COMMON_OPTS
+
+ { "height", "Set output height (0 to use the height of input 0)", OFFSET(tile_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(hstack_vaapi);
+
+const AVFilter ff_vf_hstack_vaapi = {
+ .name = "hstack_vaapi",
+ .description = NULL_IF_CONFIG_SMALL("VA-API hstack."),
+ .priv_size = sizeof(StackVAAPIContext),
+ .priv_class = &hstack_vaapi_class,
+ .init = vaapi_stack_init,
+ .uninit = vaapi_stack_uninit,
+ .activate = vaapi_stack_activate,
+ FILTER_QUERY_FUNC(vaapi_stack_query_formats),
+ FILTER_OUTPUTS(vaapi_stack_outputs),
+ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+ .flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
+};
+
+#endif
+
+#if CONFIG_VSTACK_VAAPI_FILTER
+
+static const AVOption vstack_vaapi_options[] = {
+ STACK_COMMON_OPTS
+
+ { "width", "Set output width (0 to use the width of input 0)", OFFSET(tile_width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(vstack_vaapi);
+
+const AVFilter ff_vf_vstack_vaapi = {
+ .name = "vstack_vaapi",
+ .description = NULL_IF_CONFIG_SMALL("VA-API vstack."),
+ .priv_size = sizeof(StackVAAPIContext),
+ .priv_class = &vstack_vaapi_class,
+ .init = vaapi_stack_init,
+ .uninit = vaapi_stack_uninit,
+ .activate = vaapi_stack_activate,
+ FILTER_QUERY_FUNC(vaapi_stack_query_formats),
+ FILTER_OUTPUTS(vaapi_stack_outputs),
+ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+ .flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
+};
+
+#endif
+
+#if CONFIG_XSTACK_VAAPI_FILTER
+
+static const AVOption xstack_vaapi_options[] = {
+ STACK_COMMON_OPTS
+
+ { "layout", "Set custom layout", OFFSET(layout), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS },
+ { "grid", "set fixed size grid layout", OFFSET(nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+ { "grid_tile_size", "set tile size in grid layout", OFFSET(tile_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS },
+ { "fill", "Set the color for unused pixels", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(xstack_vaapi);
+
+const AVFilter ff_vf_xstack_vaapi = {
+ .name = "xstack_vaapi",
+ .description = NULL_IF_CONFIG_SMALL("VA-API xstack."),
+ .priv_size = sizeof(StackVAAPIContext),
+ .priv_class = &xstack_vaapi_class,
+ .init = vaapi_stack_init,
+ .uninit = vaapi_stack_uninit,
+ .activate = vaapi_stack_activate,
+ FILTER_OUTPUTS(vaapi_stack_outputs),
+ FILTER_QUERY_FUNC(vaapi_stack_query_formats),
+ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+ .flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
+};
+
+#endif
1
0